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    *   https://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.ObjectUtil;
21  import io.netty.util.internal.PlatformDependent;
22  
23  import java.nio.ByteBuffer;
24  import java.nio.CharBuffer;
25  import java.nio.charset.Charset;
26  import java.nio.charset.CharsetEncoder;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.List;
30  import java.util.regex.Pattern;
31  import java.util.regex.PatternSyntaxException;
32  
33  import static io.netty.util.internal.MathUtil.isOutOfBounds;
34  import static io.netty.util.internal.ObjectUtil.checkNotNull;
35  
36  /**
37   * A string which has been encoded into a character encoding whose character always takes a single byte, similarly to
38   * ASCII. It internally keeps its content in a byte array unlike {@link String}, which uses a character array, for
39   * reduced memory footprint and faster data transfer from/to byte-based data structures such as a byte array and
40   * {@link ByteBuffer}. It is often used in conjunction with {@code Headers} that require a {@link CharSequence}.
41   * <p>
42   * This class was designed to provide an immutable array of bytes, and caches some internal state based upon the value
43   * of this array. However underlying access to this byte array is provided via not copying the array on construction or
44   * {@link #array()}. If any changes are made to the underlying byte array it is the user's responsibility to call
45   * {@link #arrayChanged()} so the state of this class can be reset.
46   */
47  public final class AsciiString implements CharSequence, Comparable<CharSequence> {
48      public static final AsciiString EMPTY_STRING = cached("");
49      private static final char MAX_CHAR_VALUE = 255;
50  
51      public static final int INDEX_NOT_FOUND = -1;
52  
53      /**
54       * If this value is modified outside the constructor then call {@link #arrayChanged()}.
55       */
56      private final byte[] value;
57      /**
58       * Offset into {@link #value} that all operations should use when acting upon {@link #value}.
59       */
60      private final int offset;
61      /**
62       * Length in bytes for {@link #value} that we care about. This is independent from {@code value.length}
63       * because we may be looking at a subsection of the array.
64       */
65      private final int length;
66      /**
67       * The hash code is cached after it is first computed. It can be reset with {@link #arrayChanged()}.
68       */
69      private int hash;
70      /**
71       * Used to cache the {@link #toString()} value.
72       */
73      private String string;
74  
75      /**
76       * Initialize this byte string based upon a byte array. A copy will be made.
77       */
78      public AsciiString(byte[] value) {
79          this(value, true);
80      }
81  
82      /**
83       * Initialize this byte string based upon a byte array.
84       * {@code copy} determines if a copy is made or the array is shared.
85       */
86      public AsciiString(byte[] value, boolean copy) {
87          this(value, 0, value.length, copy);
88      }
89  
90      /**
91       * Construct a new instance from a {@code byte[]} array.
92       * @param copy {@code true} then a copy of the memory will be made. {@code false} the underlying memory
93       * will be shared.
94       */
95      public AsciiString(byte[] value, int start, int length, boolean copy) {
96          if (copy) {
97              this.value = Arrays.copyOfRange(value, start, start + length);
98              this.offset = 0;
99          } else {
100             if (isOutOfBounds(start, length, value.length)) {
101                 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" +
102                         length + ") <= " + "value.length(" + value.length + ')');
103             }
104             this.value = value;
105             this.offset = start;
106         }
107         this.length = length;
108     }
109 
110     /**
111      * Create a copy of the underlying storage from {@code value}.
112      * The copy will start at {@link ByteBuffer#position()} and copy {@link ByteBuffer#remaining()} bytes.
113      */
114     public AsciiString(ByteBuffer value) {
115         this(value, true);
116     }
117 
118     /**
119      * Initialize an instance based upon the underlying storage from {@code value}.
120      * There is a potential to share the underlying array storage if {@link ByteBuffer#hasArray()} is {@code true}.
121      * if {@code copy} is {@code true} a copy will be made of the memory.
122      * if {@code copy} is {@code false} the underlying storage will be shared, if possible.
123      */
124     public AsciiString(ByteBuffer value, boolean copy) {
125         this(value, value.position(), value.remaining(), copy);
126     }
127 
128     /**
129      * Initialize an {@link AsciiString} based upon the underlying storage from {@code value}.
130      * There is a potential to share the underlying array storage if {@link ByteBuffer#hasArray()} is {@code true}.
131      * if {@code copy} is {@code true} a copy will be made of the memory.
132      * if {@code copy} is {@code false} the underlying storage will be shared, if possible.
133      */
134     public AsciiString(ByteBuffer value, int start, int length, boolean copy) {
135         if (isOutOfBounds(start, length, value.capacity())) {
136             throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
137                             + ") <= " + "value.capacity(" + value.capacity() + ')');
138         }
139 
140         if (value.hasArray()) {
141             if (copy) {
142                 final int bufferOffset = value.arrayOffset() + start;
143                 this.value = Arrays.copyOfRange(value.array(), bufferOffset, bufferOffset + length);
144                 offset = 0;
145             } else {
146                 this.value = value.array();
147                 this.offset = start;
148             }
149         } else {
150             this.value = PlatformDependent.allocateUninitializedArray(length);
151             int oldPos = value.position();
152             value.get(this.value, 0, length);
153             value.position(oldPos);
154             this.offset = 0;
155         }
156         this.length = length;
157     }
158 
159     /**
160      * Create a copy of {@code value} into this instance assuming ASCII encoding.
161      */
162     public AsciiString(char[] value) {
163         this(value, 0, value.length);
164     }
165 
166     /**
167      * Create a copy of {@code value} into this instance assuming ASCII encoding.
168      * The copy will start at index {@code start} and copy {@code length} bytes.
169      */
170     public AsciiString(char[] value, int start, int length) {
171         if (isOutOfBounds(start, length, value.length)) {
172             throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
173                             + ") <= " + "value.length(" + value.length + ')');
174         }
175 
176         this.value = PlatformDependent.allocateUninitializedArray(length);
177         for (int i = 0, j = start; i < length; i++, j++) {
178             this.value[i] = c2b(value[j]);
179         }
180         this.offset = 0;
181         this.length = length;
182     }
183 
184     /**
185      * Create a copy of {@code value} into this instance using the encoding type of {@code charset}.
186      */
187     public AsciiString(char[] value, Charset charset) {
188         this(value, charset, 0, value.length);
189     }
190 
191     /**
192      * Create a copy of {@code value} into a this instance using the encoding type of {@code charset}.
193      * The copy will start at index {@code start} and copy {@code length} bytes.
194      */
195     public AsciiString(char[] value, Charset charset, int start, int length) {
196         CharBuffer cbuf = CharBuffer.wrap(value, start, length);
197         CharsetEncoder encoder = CharsetUtil.encoder(charset);
198         ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
199         encoder.encode(cbuf, nativeBuffer, true);
200         final int bufferOffset = nativeBuffer.arrayOffset();
201         this.value = Arrays.copyOfRange(nativeBuffer.array(), bufferOffset, bufferOffset + nativeBuffer.position());
202         this.offset = 0;
203         this.length =  this.value.length;
204     }
205 
206     /**
207      * Create a copy of {@code value} into this instance assuming ASCII encoding.
208      */
209     public AsciiString(CharSequence value) {
210         this(value, 0, value.length());
211     }
212 
213     /**
214      * Create a copy of {@code value} into this instance assuming ASCII encoding.
215      * The copy will start at index {@code start} and copy {@code length} bytes.
216      */
217     public AsciiString(CharSequence value, int start, int length) {
218         if (isOutOfBounds(start, length, value.length())) {
219             throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
220                             + ") <= " + "value.length(" + value.length() + ')');
221         }
222 
223         this.value = PlatformDependent.allocateUninitializedArray(length);
224         for (int i = 0, j = start; i < length; i++, j++) {
225             this.value[i] = c2b(value.charAt(j));
226         }
227         this.offset = 0;
228         this.length = length;
229     }
230 
231     /**
232      * Create a copy of {@code value} into this instance using the encoding type of {@code charset}.
233      */
234     public AsciiString(CharSequence value, Charset charset) {
235         this(value, charset, 0, value.length());
236     }
237 
238     /**
239      * Create a copy of {@code value} into this instance using the encoding type of {@code charset}.
240      * The copy will start at index {@code start} and copy {@code length} bytes.
241      */
242     public AsciiString(CharSequence value, Charset charset, int start, int length) {
243         CharBuffer cbuf = CharBuffer.wrap(value, start, start + length);
244         CharsetEncoder encoder = CharsetUtil.encoder(charset);
245         ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
246         encoder.encode(cbuf, nativeBuffer, true);
247         final int offset = nativeBuffer.arrayOffset();
248         this.value = Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position());
249         this.offset = 0;
250         this.length = this.value.length;
251     }
252 
253     /**
254      * Iterates over the readable bytes of this buffer with the specified {@code processor} in ascending order.
255      *
256      * @return {@code -1} if the processor iterated to or beyond the end of the readable bytes.
257      *         The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
258      */
259     public int forEachByte(ByteProcessor visitor) throws Exception {
260         return forEachByte0(0, length(), visitor);
261     }
262 
263     /**
264      * Iterates over the specified area of this buffer with the specified {@code processor} in ascending order.
265      * (i.e. {@code index}, {@code (index + 1)},  .. {@code (index + length - 1)}).
266      *
267      * @return {@code -1} if the processor iterated to or beyond the end of the specified area.
268      *         The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
269      */
270     public int forEachByte(int index, int length, ByteProcessor visitor) throws Exception {
271         if (isOutOfBounds(index, length, length())) {
272             throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length
273                     + ") <= " + "length(" + length() + ')');
274         }
275         return forEachByte0(index, length, visitor);
276     }
277 
278     private int forEachByte0(int index, int length, ByteProcessor visitor) throws Exception {
279         final int len = offset + index + length;
280         for (int i = offset + index; i < len; ++i) {
281             if (!visitor.process(value[i])) {
282                 return i - offset;
283             }
284         }
285         return -1;
286     }
287 
288     /**
289      * Iterates over the readable bytes of this buffer with the specified {@code processor} in descending order.
290      *
291      * @return {@code -1} if the processor iterated to or beyond the beginning of the readable bytes.
292      *         The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
293      */
294     public int forEachByteDesc(ByteProcessor visitor) throws Exception {
295         return forEachByteDesc0(0, length(), visitor);
296     }
297 
298     /**
299      * Iterates over the specified area of this buffer with the specified {@code processor} in descending order.
300      * (i.e. {@code (index + length - 1)}, {@code (index + length - 2)}, ... {@code index}).
301      *
302      * @return {@code -1} if the processor iterated to or beyond the beginning of the specified area.
303      *         The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
304      */
305     public int forEachByteDesc(int index, int length, ByteProcessor visitor) throws Exception {
306         if (isOutOfBounds(index, length, length())) {
307             throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length
308                     + ") <= " + "length(" + length() + ')');
309         }
310         return forEachByteDesc0(index, length, visitor);
311     }
312 
313     private int forEachByteDesc0(int index, int length, ByteProcessor visitor) throws Exception {
314         final int end = offset + index;
315         for (int i = offset + index + length - 1; i >= end; --i) {
316             if (!visitor.process(value[i])) {
317                 return i - offset;
318             }
319         }
320         return -1;
321     }
322 
323     public byte byteAt(int index) {
324         // We must do a range check here to enforce the access does not go outside our sub region of the array.
325         // We rely on the array access itself to pick up the array out of bounds conditions
326         if (index < 0 || index >= length) {
327             throw new IndexOutOfBoundsException("index: " + index + " must be in the range [0," + length + ")");
328         }
329         // Try to use unsafe to avoid double checking the index bounds
330         if (PlatformDependent.hasUnsafe()) {
331             return PlatformDependent.getByte(value, index + offset);
332         }
333         return value[index + offset];
334     }
335 
336     /**
337      * Determine if this instance has 0 length.
338      */
339     public boolean isEmpty() {
340         return length == 0;
341     }
342 
343     /**
344      * The length in bytes of this instance.
345      */
346     @Override
347     public int length() {
348         return length;
349     }
350 
351     /**
352      * During normal use cases the {@link AsciiString} should be immutable, but if the underlying array is shared,
353      * and changes then this needs to be called.
354      */
355     public void arrayChanged() {
356         string = null;
357         hash = 0;
358     }
359 
360     /**
361      * This gives direct access to the underlying storage array.
362      * The {@link #toByteArray()} should be preferred over this method.
363      * If the return value is changed then {@link #arrayChanged()} must be called.
364      * @see #arrayOffset()
365      * @see #isEntireArrayUsed()
366      */
367     public byte[] array() {
368         return value;
369     }
370 
371     /**
372      * The offset into {@link #array()} for which data for this ByteString begins.
373      * @see #array()
374      * @see #isEntireArrayUsed()
375      */
376     public int arrayOffset() {
377         return offset;
378     }
379 
380     /**
381      * Determine if the storage represented by {@link #array()} is entirely used.
382      * @see #array()
383      */
384     public boolean isEntireArrayUsed() {
385         return offset == 0 && length == value.length;
386     }
387 
388     /**
389      * Converts this string to a byte array.
390      */
391     public byte[] toByteArray() {
392         return toByteArray(0, length());
393     }
394 
395     /**
396      * Converts a subset of this string to a byte array.
397      * The subset is defined by the range [{@code start}, {@code end}).
398      */
399     public byte[] toByteArray(int start, int end) {
400         return Arrays.copyOfRange(value, start + offset, end + offset);
401     }
402 
403     /**
404      * Copies the content of this string to a byte array.
405      *
406      * @param srcIdx the starting offset of characters to copy.
407      * @param dst the destination byte array.
408      * @param dstIdx the starting offset in the destination byte array.
409      * @param length the number of characters to copy.
410      */
411     public void copy(int srcIdx, byte[] dst, int dstIdx, int length) {
412         if (isOutOfBounds(srcIdx, length, length())) {
413             throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
414                             + length + ") <= srcLen(" + length() + ')');
415         }
416 
417         System.arraycopy(value, srcIdx + offset, checkNotNull(dst, "dst"), dstIdx, length);
418     }
419 
420     @Override
421     public char charAt(int index) {
422         return b2c(byteAt(index));
423     }
424 
425     /**
426      * Determines if this {@code String} contains the sequence of characters in the {@code CharSequence} passed.
427      *
428      * @param cs the character sequence to search for.
429      * @return {@code true} if the sequence of characters are contained in this string, otherwise {@code false}.
430      */
431     public boolean contains(CharSequence cs) {
432         return indexOf(cs) >= 0;
433     }
434 
435     /**
436      * Compares the specified string to this string using the ASCII values of the characters. Returns 0 if the strings
437      * contain the same characters in the same order. Returns a negative integer if the first non-equal character in
438      * this string has an ASCII value which is less than the ASCII value of the character at the same position in the
439      * specified string, or if this string is a prefix of the specified string. Returns a positive integer if the first
440      * non-equal character in this string has a ASCII value which is greater than the ASCII value of the character at
441      * the same position in the specified string, or if the specified string is a prefix of this string.
442      *
443      * @param string the string to compare.
444      * @return 0 if the strings are equal, a negative integer if this string is before the specified string, or a
445      *         positive integer if this string is after the specified string.
446      * @throws NullPointerException if {@code string} is {@code null}.
447      */
448     @Override
449     public int compareTo(CharSequence string) {
450         if (this == string) {
451             return 0;
452         }
453 
454         int result;
455         int length1 = length();
456         int length2 = string.length();
457         int minLength = Math.min(length1, length2);
458         for (int i = 0, j = arrayOffset(); i < minLength; i++, j++) {
459             result = b2c(value[j]) - string.charAt(i);
460             if (result != 0) {
461                 return result;
462             }
463         }
464 
465         return length1 - length2;
466     }
467 
468     /**
469      * Concatenates this string and the specified string.
470      *
471      * @param string the string to concatenate
472      * @return a new string which is the concatenation of this string and the specified string.
473      */
474     public AsciiString concat(CharSequence string) {
475         int thisLen = length();
476         int thatLen = string.length();
477         if (thatLen == 0) {
478             return this;
479         }
480 
481         if (string instanceof AsciiString) {
482             AsciiString that = (AsciiString) string;
483             if (isEmpty()) {
484                 return that;
485             }
486 
487             byte[] newValue = PlatformDependent.allocateUninitializedArray(thisLen + thatLen);
488             System.arraycopy(value, arrayOffset(), newValue, 0, thisLen);
489             System.arraycopy(that.value, that.arrayOffset(), newValue, thisLen, thatLen);
490             return new AsciiString(newValue, false);
491         }
492 
493         if (isEmpty()) {
494             return new AsciiString(string);
495         }
496 
497         byte[] newValue = PlatformDependent.allocateUninitializedArray(thisLen + thatLen);
498         System.arraycopy(value, arrayOffset(), newValue, 0, thisLen);
499         for (int i = thisLen, j = 0; i < newValue.length; i++, j++) {
500             newValue[i] = c2b(string.charAt(j));
501         }
502 
503         return new AsciiString(newValue, false);
504     }
505 
506     /**
507      * Compares the specified string to this string to determine if the specified string is a suffix.
508      *
509      * @param suffix the suffix to look for.
510      * @return {@code true} if the specified string is a suffix of this string, {@code false} otherwise.
511      * @throws NullPointerException if {@code suffix} is {@code null}.
512      */
513     public boolean endsWith(CharSequence suffix) {
514         int suffixLen = suffix.length();
515         return regionMatches(length() - suffixLen, suffix, 0, suffixLen);
516     }
517 
518     /**
519      * Compares the specified string to this string ignoring the case of the characters and returns true if they are
520      * equal.
521      *
522      * @param string the string to compare.
523      * @return {@code true} if the specified string is equal to this string, {@code false} otherwise.
524      */
525     public boolean contentEqualsIgnoreCase(CharSequence string) {
526         if (this == string) {
527             return true;
528         }
529 
530         if (string == null || string.length() != length()) {
531             return false;
532         }
533 
534         if (string instanceof AsciiString) {
535             AsciiString rhs = (AsciiString) string;
536             for (int i = arrayOffset(), j = rhs.arrayOffset(), end = i + length(); i < end; ++i, ++j) {
537                 if (!equalsIgnoreCase(value[i], rhs.value[j])) {
538                     return false;
539                 }
540             }
541             return true;
542         }
543 
544         for (int i = arrayOffset(), j = 0, end = length(); j < end; ++i, ++j) {
545             if (!equalsIgnoreCase(b2c(value[i]), string.charAt(j))) {
546                 return false;
547             }
548         }
549         return true;
550     }
551 
552     /**
553      * Copies the characters in this string to a character array.
554      *
555      * @return a character array containing the characters of this string.
556      */
557     public char[] toCharArray() {
558         return toCharArray(0, length());
559     }
560 
561     /**
562      * Copies the characters in this string to a character array.
563      *
564      * @return a character array containing the characters of this string.
565      */
566     public char[] toCharArray(int start, int end) {
567         int length = end - start;
568         if (length == 0) {
569             return EmptyArrays.EMPTY_CHARS;
570         }
571 
572         if (isOutOfBounds(start, length, length())) {
573             throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
574                             + length + ") <= srcLen(" + length() + ')');
575         }
576 
577         final char[] buffer = new char[length];
578         for (int i = 0, j = start + arrayOffset(); i < length; i++, j++) {
579             buffer[i] = b2c(value[j]);
580         }
581         return buffer;
582     }
583 
584     /**
585      * Copied the content of this string to a character array.
586      *
587      * @param srcIdx the starting offset of characters to copy.
588      * @param dst the destination character array.
589      * @param dstIdx the starting offset in the destination byte array.
590      * @param length the number of characters to copy.
591      */
592     public void copy(int srcIdx, char[] dst, int dstIdx, int length) {
593         ObjectUtil.checkNotNull(dst, "dst");
594 
595         if (isOutOfBounds(srcIdx, length, length())) {
596             throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
597                             + length + ") <= srcLen(" + length() + ')');
598         }
599 
600         final int dstEnd = dstIdx + length;
601         for (int i = dstIdx, j = srcIdx + arrayOffset(); i < dstEnd; i++, j++) {
602             dst[i] = b2c(value[j]);
603         }
604     }
605 
606     /**
607      * Copies a range of characters into a new string.
608      * @param start the offset of the first character (inclusive).
609      * @return a new string containing the characters from start to the end of the string.
610      * @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
611      */
612     public AsciiString subSequence(int start) {
613         return subSequence(start, length());
614     }
615 
616     /**
617      * Copies a range of characters into a new string.
618      * @param start the offset of the first character (inclusive).
619      * @param end The index to stop at (exclusive).
620      * @return a new string containing the characters from start to the end of the string.
621      * @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
622      */
623     @Override
624     public AsciiString subSequence(int start, int end) {
625        return subSequence(start, end, true);
626     }
627 
628     /**
629      * Either copy or share a subset of underlying sub-sequence of bytes.
630      * @param start the offset of the first character (inclusive).
631      * @param end The index to stop at (exclusive).
632      * @param copy If {@code true} then a copy of the underlying storage will be made.
633      * If {@code false} then the underlying storage will be shared.
634      * @return a new string containing the characters from start to the end of the string.
635      * @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
636      */
637     public AsciiString subSequence(int start, int end, boolean copy) {
638         if (isOutOfBounds(start, end - start, length())) {
639             throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length("
640                             + length() + ')');
641         }
642 
643         if (start == 0 && end == length()) {
644             return this;
645         }
646 
647         if (end == start) {
648             return EMPTY_STRING;
649         }
650 
651         return new AsciiString(value, start + offset, end - start, copy);
652     }
653 
654     /**
655      * Searches in this string for the first index of the specified string. The search for the string starts at the
656      * beginning and moves towards the end of this string.
657      *
658      * @param string the string to find.
659      * @return the index of the first character of the specified string in this string, -1 if the specified string is
660      *         not a substring.
661      * @throws NullPointerException if {@code string} is {@code null}.
662      */
663     public int indexOf(CharSequence string) {
664         return indexOf(string, 0);
665     }
666 
667     /**
668      * Searches in this string for the index of the specified string. The search for the string starts at the specified
669      * offset and moves towards the end of this string.
670      *
671      * @param subString the string to find.
672      * @param start the starting offset.
673      * @return the index of the first character of the specified string in this string, -1 if the specified string is
674      *         not a substring.
675      * @throws NullPointerException if {@code subString} is {@code null}.
676      */
677     public int indexOf(CharSequence subString, int start) {
678         final int subCount = subString.length();
679         if (start < 0) {
680             start = 0;
681         }
682         if (subCount <= 0) {
683             return start < length ? start : length;
684         }
685         if (subCount > length - start) {
686             return INDEX_NOT_FOUND;
687         }
688 
689         final char firstChar = subString.charAt(0);
690         if (firstChar > MAX_CHAR_VALUE) {
691             return INDEX_NOT_FOUND;
692         }
693         final byte firstCharAsByte = c2b0(firstChar);
694         final int len = offset + length - subCount;
695         for (int i = start + offset; i <= len; ++i) {
696             if (value[i] == firstCharAsByte) {
697                 int o1 = i, o2 = 0;
698                 while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
699                     // Intentionally empty
700                 }
701                 if (o2 == subCount) {
702                     return i - offset;
703                 }
704             }
705         }
706         return INDEX_NOT_FOUND;
707     }
708 
709     /**
710      * Searches in this string for the index of the specified char {@code ch}.
711      * The search for the char starts at the specified offset {@code start} and moves towards the end of this string.
712      *
713      * @param ch the char to find.
714      * @param start the starting offset.
715      * @return the index of the first occurrence of the specified char {@code ch} in this string,
716      * -1 if found no occurrence.
717      */
718     public int indexOf(char ch, int start) {
719         if (ch > MAX_CHAR_VALUE) {
720             return INDEX_NOT_FOUND;
721         }
722 
723         if (start < 0) {
724             start = 0;
725         }
726 
727         final byte chAsByte = c2b0(ch);
728         final int len = offset + length;
729         for (int i = start + offset; i < len; ++i) {
730             if (value[i] == chAsByte) {
731                 return i - offset;
732             }
733         }
734         return INDEX_NOT_FOUND;
735     }
736 
737     /**
738      * Searches in this string for the last index of the specified string. The search for the string starts at the end
739      * and moves towards the beginning of this string.
740      *
741      * @param string the string to find.
742      * @return the index of the first character of the specified string in this string, -1 if the specified string is
743      *         not a substring.
744      * @throws NullPointerException if {@code string} is {@code null}.
745      */
746     public int lastIndexOf(CharSequence string) {
747         // Use count instead of count - 1 so lastIndexOf("") answers count
748         return lastIndexOf(string, length);
749     }
750 
751     /**
752      * Searches in this string for the index of the specified string. The search for the string starts at the specified
753      * offset and moves towards the beginning of this string.
754      *
755      * @param subString the string to find.
756      * @param start the starting offset.
757      * @return the index of the first character of the specified string in this string , -1 if the specified string is
758      *         not a substring.
759      * @throws NullPointerException if {@code subString} is {@code null}.
760      */
761     public int lastIndexOf(CharSequence subString, int start) {
762         final int subCount = subString.length();
763         start = Math.min(start, length - subCount);
764         if (start < 0) {
765             return INDEX_NOT_FOUND;
766         }
767         if (subCount == 0) {
768             return start;
769         }
770 
771         final char firstChar = subString.charAt(0);
772         if (firstChar > MAX_CHAR_VALUE) {
773             return INDEX_NOT_FOUND;
774         }
775         final byte firstCharAsByte = c2b0(firstChar);
776         for (int i = offset + start; i >= 0; --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         ObjectUtil.checkNotNull(string, "string");
803 
804         if (start < 0 || string.length() - start < length) {
805             return false;
806         }
807 
808         final int thisLen = length();
809         if (thisStart < 0 || thisLen - thisStart < length) {
810             return false;
811         }
812 
813         if (length <= 0) {
814             return true;
815         }
816 
817         final int thatEnd = start + length;
818         for (int i = start, j = thisStart + arrayOffset(); i < thatEnd; i++, j++) {
819             if (b2c(value[j]) != string.charAt(i)) {
820                 return false;
821             }
822         }
823         return true;
824     }
825 
826     /**
827      * Compares the specified string to this string and compares the specified range of characters to determine if they
828      * are the same. When ignoreCase is true, the case of the characters is ignored during the comparison.
829      *
830      * @param ignoreCase specifies if case should be ignored.
831      * @param thisStart the starting offset in this string.
832      * @param string the string to compare.
833      * @param start the starting offset in the specified string.
834      * @param length the number of characters to compare.
835      * @return {@code true} if the ranges of characters are equal, {@code false} otherwise.
836      * @throws NullPointerException if {@code string} is {@code null}.
837      */
838     public boolean regionMatches(boolean ignoreCase, int thisStart, CharSequence string, int start, int length) {
839         if (!ignoreCase) {
840             return regionMatches(thisStart, string, start, length);
841         }
842 
843         ObjectUtil.checkNotNull(string, "string");
844 
845         final int thisLen = length();
846         if (thisStart < 0 || length > thisLen - thisStart) {
847             return false;
848         }
849         if (start < 0 || length > string.length() - start) {
850             return false;
851         }
852 
853         thisStart += arrayOffset();
854         final int thisEnd = thisStart + length;
855         while (thisStart < thisEnd) {
856             if (!equalsIgnoreCase(b2c(value[thisStart++]), string.charAt(start++))) {
857                 return false;
858             }
859         }
860         return true;
861     }
862 
863     /**
864      * Copies this string replacing occurrences of the specified character with another character.
865      *
866      * @param oldChar the character to replace.
867      * @param newChar the replacement character.
868      * @return a new string with occurrences of oldChar replaced by newChar.
869      */
870     public AsciiString replace(char oldChar, char newChar) {
871         if (oldChar > MAX_CHAR_VALUE) {
872             return this;
873         }
874 
875         final byte oldCharAsByte = c2b0(oldChar);
876         final byte newCharAsByte = c2b(newChar);
877         final int len = offset + length;
878         for (int i = offset; i < len; ++i) {
879             if (value[i] == oldCharAsByte) {
880                 byte[] buffer = PlatformDependent.allocateUninitializedArray(length());
881                 System.arraycopy(value, offset, buffer, 0, i - offset);
882                 buffer[i - offset] = newCharAsByte;
883                 ++i;
884                 for (; i < len; ++i) {
885                     byte oldValue = value[i];
886                     buffer[i - offset] = oldValue != oldCharAsByte ? oldValue : newCharAsByte;
887                 }
888                 return new AsciiString(buffer, false);
889             }
890         }
891         return this;
892     }
893 
894     /**
895      * Compares the specified string to this string to determine if the specified string is a prefix.
896      *
897      * @param prefix the string to look for.
898      * @return {@code true} if the specified string is a prefix of this string, {@code false} otherwise
899      * @throws NullPointerException if {@code prefix} is {@code null}.
900      */
901     public boolean startsWith(CharSequence prefix) {
902         return startsWith(prefix, 0);
903     }
904 
905     /**
906      * Compares the specified string to this string, starting at the specified offset, to determine if the specified
907      * string is a prefix.
908      *
909      * @param prefix the string to look for.
910      * @param start the starting offset.
911      * @return {@code true} if the specified string occurs in this string at the specified offset, {@code false}
912      *         otherwise.
913      * @throws NullPointerException if {@code prefix} is {@code null}.
914      */
915     public boolean startsWith(CharSequence prefix, int start) {
916         return regionMatches(start, prefix, 0, prefix.length());
917     }
918 
919     /**
920      * Converts the characters in this string to lowercase, using the default Locale.
921      *
922      * @return a new string containing the lowercase characters equivalent to the characters in this string.
923      */
924     public AsciiString toLowerCase() {
925         boolean lowercased = true;
926         int i, j;
927         final int len = length() + arrayOffset();
928         for (i = arrayOffset(); i < len; ++i) {
929             byte b = value[i];
930             if (b >= 'A' && b <= 'Z') {
931                 lowercased = false;
932                 break;
933             }
934         }
935 
936         // Check if this string does not contain any uppercase characters.
937         if (lowercased) {
938             return this;
939         }
940 
941         final byte[] newValue = PlatformDependent.allocateUninitializedArray(length());
942         for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) {
943             newValue[i] = toLowerCase(value[j]);
944         }
945 
946         return new AsciiString(newValue, false);
947     }
948 
949     /**
950      * Converts the characters in this string to uppercase, using the default Locale.
951      *
952      * @return a new string containing the uppercase characters equivalent to the characters in this string.
953      */
954     public AsciiString toUpperCase() {
955         boolean uppercased = true;
956         int i, j;
957         final int len = length() + arrayOffset();
958         for (i = arrayOffset(); i < len; ++i) {
959             byte b = value[i];
960             if (b >= 'a' && b <= 'z') {
961                 uppercased = false;
962                 break;
963             }
964         }
965 
966         // Check if this string does not contain any lowercase characters.
967         if (uppercased) {
968             return this;
969         }
970 
971         final byte[] newValue = PlatformDependent.allocateUninitializedArray(length());
972         for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) {
973             newValue[i] = toUpperCase(value[j]);
974         }
975 
976         return new AsciiString(newValue, false);
977     }
978 
979     /**
980      * Copies this string removing white space characters from the beginning and end of the string, and tries not to
981      * copy if possible.
982      *
983      * @param c The {@link CharSequence} to trim.
984      * @return a new string with characters {@code <= \\u0020} removed from the beginning and the end.
985      */
986     public static CharSequence trim(CharSequence c) {
987         if (c instanceof AsciiString) {
988             return ((AsciiString) c).trim();
989         }
990         if (c instanceof String) {
991             return ((String) c).trim();
992         }
993         int start = 0, last = c.length() - 1;
994         int end = last;
995         while (start <= end && c.charAt(start) <= ' ') {
996             start++;
997         }
998         while (end >= start && c.charAt(end) <= ' ') {
999             end--;
1000         }
1001         if (start == 0 && end == last) {
1002             return c;
1003         }
1004         return c.subSequence(start, end);
1005     }
1006 
1007     /**
1008      * Duplicates this string removing white space characters from the beginning and end of the
1009      * string, without copying.
1010      *
1011      * @return a new string with characters {@code <= \\u0020} removed from the beginning and the end.
1012      */
1013     public AsciiString trim() {
1014         int start = arrayOffset(), last = arrayOffset() + length() - 1;
1015         int end = last;
1016         while (start <= end && value[start] <= ' ') {
1017             start++;
1018         }
1019         while (end >= start && value[end] <= ' ') {
1020             end--;
1021         }
1022         if (start == 0 && end == last) {
1023             return this;
1024         }
1025         return new AsciiString(value, start, end - start + 1, false);
1026     }
1027 
1028     /**
1029      * Compares a {@code CharSequence} to this {@code String} to determine if their contents are equal.
1030      *
1031      * @param a the character sequence to compare to.
1032      * @return {@code true} if equal, otherwise {@code false}
1033      */
1034     public boolean contentEquals(CharSequence a) {
1035         if (this == a) {
1036             return true;
1037         }
1038 
1039         if (a == null || a.length() != length()) {
1040             return false;
1041         }
1042         if (a instanceof AsciiString) {
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 instanceof AsciiString ? (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 instanceof AsciiString) {
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 instanceof AsciiString) {
1446             return ((AsciiString) a).contentEqualsIgnoreCase(b);
1447         }
1448         if (b instanceof AsciiString) {
1449             return ((AsciiString) b).contentEqualsIgnoreCase(a);
1450         }
1451 
1452         if (a.length() != b.length()) {
1453             return false;
1454         }
1455         for (int i = 0; i < a.length(); ++i) {
1456             if (!equalsIgnoreCase(a.charAt(i),  b.charAt(i))) {
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 instanceof AsciiString) {
1508             return ((AsciiString) a).contentEquals(b);
1509         }
1510 
1511         if (b instanceof AsciiString) {
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     /**
1831      * If the character is uppercase - converts the character to lowercase,
1832      * otherwise returns the character as it is. Only for ASCII characters.
1833      *
1834      * @return lowercase ASCII character equivalent
1835      */
1836     public static char toLowerCase(char c) {
1837         return isUpperCase(c) ? (char) (c + 32) : c;
1838     }
1839 
1840     private static byte toUpperCase(byte b) {
1841         return isLowerCase(b) ? (byte) (b - 32) : b;
1842     }
1843 
1844     private static boolean isLowerCase(byte value) {
1845         return value >= 'a' && value <= 'z';
1846     }
1847 
1848     public static boolean isUpperCase(byte value) {
1849         return value >= 'A' && value <= 'Z';
1850     }
1851 
1852     public static boolean isUpperCase(char value) {
1853         return value >= 'A' && value <= 'Z';
1854     }
1855 
1856     public static byte c2b(char c) {
1857         return (byte) ((c > MAX_CHAR_VALUE) ? '?' : c);
1858     }
1859 
1860     private static byte c2b0(char c) {
1861         return (byte) c;
1862     }
1863 
1864     public static char b2c(byte b) {
1865         return (char) (b & 0xFF);
1866     }
1867 }