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.ByteProcessor.IndexOfProcessor;
19  import io.netty.util.internal.EmptyArrays;
20  import io.netty.util.internal.InternalThreadLocalMap;
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 = new byte[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 = new byte[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 = new byte[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.getClass() == AsciiString.class) {
482             AsciiString that = (AsciiString) string;
483             if (isEmpty()) {
484                 return that;
485             }
486 
487             byte[] newValue = new byte[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 = new byte[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 (string == null || string.length() != length()) {
527             return false;
528         }
529 
530         if (string.getClass() == AsciiString.class) {
531             AsciiString rhs = (AsciiString) string;
532             for (int i = arrayOffset(), j = rhs.arrayOffset(); i < length(); ++i, ++j) {
533                 if (!equalsIgnoreCase(value[i], rhs.value[j])) {
534                     return false;
535                 }
536             }
537             return true;
538         }
539 
540         for (int i = arrayOffset(), j = 0; i < length(); ++i, ++j) {
541             if (!equalsIgnoreCase(b2c(value[i]), string.charAt(j))) {
542                 return false;
543             }
544         }
545         return true;
546     }
547 
548     /**
549      * Copies the characters in this string to a character array.
550      *
551      * @return a character array containing the characters of this string.
552      */
553     public char[] toCharArray() {
554         return toCharArray(0, length());
555     }
556 
557     /**
558      * Copies the characters in this string to a character array.
559      *
560      * @return a character array containing the characters of this string.
561      */
562     public char[] toCharArray(int start, int end) {
563         int length = end - start;
564         if (length == 0) {
565             return EmptyArrays.EMPTY_CHARS;
566         }
567 
568         if (isOutOfBounds(start, length, length())) {
569             throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
570                             + length + ") <= srcLen(" + length() + ')');
571         }
572 
573         final char[] buffer = new char[length];
574         for (int i = 0, j = start + arrayOffset(); i < length; i++, j++) {
575             buffer[i] = b2c(value[j]);
576         }
577         return buffer;
578     }
579 
580     /**
581      * Copied the content of this string to a character array.
582      *
583      * @param srcIdx the starting offset of characters to copy.
584      * @param dst the destination character array.
585      * @param dstIdx the starting offset in the destination byte array.
586      * @param length the number of characters to copy.
587      */
588     public void copy(int srcIdx, char[] dst, int dstIdx, int length) {
589         if (dst == null) {
590             throw new NullPointerException("dst");
591         }
592 
593         if (isOutOfBounds(srcIdx, length, length())) {
594             throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
595                             + length + ") <= srcLen(" + length() + ')');
596         }
597 
598         final int dstEnd = dstIdx + length;
599         for (int i = dstIdx, j = srcIdx + arrayOffset(); i < dstEnd; i++, j++) {
600             dst[i] = b2c(value[j]);
601         }
602     }
603 
604     /**
605      * Copies a range of characters into a new string.
606      * @param start the offset of the first character (inclusive).
607      * @return a new string containing the characters from start to the end of the string.
608      * @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
609      */
610     public AsciiString subSequence(int start) {
611         return subSequence(start, length());
612     }
613 
614     /**
615      * Copies a range of characters into a new string.
616      * @param start the offset of the first character (inclusive).
617      * @param end The index to stop at (exclusive).
618      * @return a new string containing the characters from start to the end of the string.
619      * @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
620      */
621     @Override
622     public AsciiString subSequence(int start, int end) {
623        return subSequence(start, end, true);
624     }
625 
626     /**
627      * Either copy or share a subset of underlying sub-sequence of bytes.
628      * @param start the offset of the first character (inclusive).
629      * @param end The index to stop at (exclusive).
630      * @param copy If {@code true} then a copy of the underlying storage will be made.
631      * If {@code false} then the underlying storage will be shared.
632      * @return a new string containing the characters from start to the end of the string.
633      * @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
634      */
635     public AsciiString subSequence(int start, int end, boolean copy) {
636         if (isOutOfBounds(start, end - start, length())) {
637             throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length("
638                             + length() + ')');
639         }
640 
641         if (start == 0 && end == length()) {
642             return this;
643         }
644 
645         if (end == start) {
646             return EMPTY_STRING;
647         }
648 
649         return new AsciiString(value, start + offset, end - start, copy);
650     }
651 
652     /**
653      * Searches in this string for the first index of the specified string. The search for the string starts at the
654      * beginning and moves towards the end of this string.
655      *
656      * @param string the string to find.
657      * @return the index of the first character of the specified string in this string, -1 if the specified string is
658      *         not a substring.
659      * @throws NullPointerException if {@code string} is {@code null}.
660      */
661     public int indexOf(CharSequence string) {
662         return indexOf(string, 0);
663     }
664 
665     /**
666      * Searches in this string for the index of the specified string. The search for the string starts at the specified
667      * offset and moves towards the end of this string.
668      *
669      * @param subString the string to find.
670      * @param start the starting offset.
671      * @return the index of the first character of the specified string in this string, -1 if the specified string is
672      *         not a substring.
673      * @throws NullPointerException if {@code subString} is {@code null}.
674      */
675     public int indexOf(CharSequence subString, int start) {
676         if (start < 0) {
677             start = 0;
678         }
679 
680         final int thisLen = length();
681 
682         int subCount = subString.length();
683         if (subCount <= 0) {
684             return start < thisLen ? start : thisLen;
685         }
686         if (subCount > thisLen - start) {
687             return -1;
688         }
689 
690         final char firstChar = subString.charAt(0);
691         if (firstChar > MAX_CHAR_VALUE) {
692             return -1;
693         }
694         ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar);
695         try {
696             for (;;) {
697                 int i = forEachByte(start, thisLen - start, IndexOfVisitor);
698                 if (i == -1 || subCount + i > thisLen) {
699                     return -1; // handles subCount > count || start >= count
700                 }
701                 int o1 = i, o2 = 0;
702                 while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) {
703                     // Intentionally empty
704                 }
705                 if (o2 == subCount) {
706                     return i;
707                 }
708                 start = i + 1;
709             }
710         } catch (Exception e) {
711             PlatformDependent.throwException(e);
712             return -1;
713         }
714     }
715 
716     /**
717      * Searches in this string for the index of the specified char {@code ch}.
718      * The search for the char starts at the specified offset {@code start} and moves towards the end of this string.
719      *
720      * @param ch the char to find.
721      * @param start the starting offset.
722      * @return the index of the first occurrence of the specified char {@code ch} in this string,
723      * -1 if found no occurrence.
724      */
725     public int indexOf(char ch, int start) {
726         if (start < 0) {
727             start = 0;
728         }
729 
730         final int thisLen = length();
731 
732         if (ch > MAX_CHAR_VALUE) {
733             return -1;
734         }
735 
736         try {
737             return forEachByte(start, thisLen - start, new IndexOfProcessor((byte) ch));
738         } catch (Exception e) {
739             PlatformDependent.throwException(e);
740             return -1;
741         }
742     }
743 
744     /**
745      * Searches in this string for the last index of the specified string. The search for the string starts at the end
746      * and moves towards the beginning of this string.
747      *
748      * @param string the string to find.
749      * @return the index of the first character of the specified string in this string, -1 if the specified string is
750      *         not a substring.
751      * @throws NullPointerException if {@code string} is {@code null}.
752      */
753     public int lastIndexOf(CharSequence string) {
754         // Use count instead of count - 1 so lastIndexOf("") answers count
755         return lastIndexOf(string, length());
756     }
757 
758     /**
759      * Searches in this string for the index of the specified string. The search for the string starts at the specified
760      * offset and moves towards the beginning of this string.
761      *
762      * @param subString the string to find.
763      * @param start the starting offset.
764      * @return the index of the first character of the specified string in this string , -1 if the specified string is
765      *         not a substring.
766      * @throws NullPointerException if {@code subString} is {@code null}.
767      */
768     public int lastIndexOf(CharSequence subString, int start) {
769         final int thisLen = length();
770         final int subCount = subString.length();
771 
772         if (subCount > thisLen || start < 0) {
773             return -1;
774         }
775 
776         if (subCount <= 0) {
777             return start < thisLen ? start : thisLen;
778         }
779 
780         start = Math.min(start, thisLen - subCount);
781 
782         // count and subCount are both >= 1
783         final char firstChar = subString.charAt(0);
784         if (firstChar > MAX_CHAR_VALUE) {
785             return -1;
786         }
787         ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar);
788         try {
789             for (;;) {
790                 int i = forEachByteDesc(start, thisLen - start, IndexOfVisitor);
791                 if (i == -1) {
792                     return -1;
793                 }
794                 int o1 = i, o2 = 0;
795                 while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) {
796                     // Intentionally empty
797                 }
798                 if (o2 == subCount) {
799                     return i;
800                 }
801                 start = i - 1;
802             }
803         } catch (Exception e) {
804             PlatformDependent.throwException(e);
805             return -1;
806         }
807     }
808 
809     /**
810      * Compares the specified string to this string and compares the specified range of characters to determine if they
811      * are the same.
812      *
813      * @param thisStart the starting offset in this string.
814      * @param string the string to compare.
815      * @param start the starting offset in the specified string.
816      * @param length the number of characters to compare.
817      * @return {@code true} if the ranges of characters are equal, {@code false} otherwise
818      * @throws NullPointerException if {@code string} is {@code null}.
819      */
820     public boolean regionMatches(int thisStart, CharSequence string, int start, int length) {
821         if (string == null) {
822             throw new NullPointerException("string");
823         }
824 
825         if (start < 0 || string.length() - start < length) {
826             return false;
827         }
828 
829         final int thisLen = length();
830         if (thisStart < 0 || thisLen - thisStart < length) {
831             return false;
832         }
833 
834         if (length <= 0) {
835             return true;
836         }
837 
838         final int thatEnd = start + length;
839         for (int i = start, j = thisStart + arrayOffset(); i < thatEnd; i++, j++) {
840             if (b2c(value[j]) != string.charAt(i)) {
841                 return false;
842             }
843         }
844         return true;
845     }
846 
847     /**
848      * Compares the specified string to this string and compares the specified range of characters to determine if they
849      * are the same. When ignoreCase is true, the case of the characters is ignored during the comparison.
850      *
851      * @param ignoreCase specifies if case should be ignored.
852      * @param thisStart the starting offset in this string.
853      * @param string the string to compare.
854      * @param start the starting offset in the specified string.
855      * @param length the number of characters to compare.
856      * @return {@code true} if the ranges of characters are equal, {@code false} otherwise.
857      * @throws NullPointerException if {@code string} is {@code null}.
858      */
859     public boolean regionMatches(boolean ignoreCase, int thisStart, CharSequence string, int start, int length) {
860         if (!ignoreCase) {
861             return regionMatches(thisStart, string, start, length);
862         }
863 
864         if (string == null) {
865             throw new NullPointerException("string");
866         }
867 
868         final int thisLen = length();
869         if (thisStart < 0 || length > thisLen - thisStart) {
870             return false;
871         }
872         if (start < 0 || length > string.length() - start) {
873             return false;
874         }
875 
876         thisStart += arrayOffset();
877         final int thisEnd = thisStart + length;
878         while (thisStart < thisEnd) {
879             if (!equalsIgnoreCase(b2c(value[thisStart++]), string.charAt(start++))) {
880                 return false;
881             }
882         }
883         return true;
884     }
885 
886     /**
887      * Copies this string replacing occurrences of the specified character with another character.
888      *
889      * @param oldChar the character to replace.
890      * @param newChar the replacement character.
891      * @return a new string with occurrences of oldChar replaced by newChar.
892      */
893     public AsciiString replace(char oldChar, char newChar) {
894         if (oldChar > MAX_CHAR_VALUE) {
895             return this;
896         }
897 
898         final int index;
899         final byte oldCharByte = c2b(oldChar);
900         try {
901             index = forEachByte(new IndexOfProcessor(oldCharByte));
902         } catch (Exception e) {
903             PlatformDependent.throwException(e);
904             return this;
905         }
906         if (index == -1) {
907             return this;
908         }
909 
910         final byte newCharByte = c2b(newChar);
911         byte[] buffer = new byte[length()];
912         for (int i = 0, j = arrayOffset(); i < buffer.length; i++, j++) {
913             byte b = value[j];
914             if (b == oldCharByte) {
915                 b = newCharByte;
916             }
917             buffer[i] = b;
918         }
919 
920         return new AsciiString(buffer, false);
921     }
922 
923     /**
924      * Compares the specified string to this string to determine if the specified string is a prefix.
925      *
926      * @param prefix the string to look for.
927      * @return {@code true} if the specified string is a prefix of this string, {@code false} otherwise
928      * @throws NullPointerException if {@code prefix} is {@code null}.
929      */
930     public boolean startsWith(CharSequence prefix) {
931         return startsWith(prefix, 0);
932     }
933 
934     /**
935      * Compares the specified string to this string, starting at the specified offset, to determine if the specified
936      * string is a prefix.
937      *
938      * @param prefix the string to look for.
939      * @param start the starting offset.
940      * @return {@code true} if the specified string occurs in this string at the specified offset, {@code false}
941      *         otherwise.
942      * @throws NullPointerException if {@code prefix} is {@code null}.
943      */
944     public boolean startsWith(CharSequence prefix, int start) {
945         return regionMatches(start, prefix, 0, prefix.length());
946     }
947 
948     /**
949      * Converts the characters in this string to lowercase, using the default Locale.
950      *
951      * @return a new string containing the lowercase characters equivalent to the characters in this string.
952      */
953     public AsciiString toLowerCase() {
954         boolean lowercased = true;
955         int i, j;
956         final int len = length() + arrayOffset();
957         for (i = arrayOffset(); i < len; ++i) {
958             byte b = value[i];
959             if (b >= 'A' && b <= 'Z') {
960                 lowercased = false;
961                 break;
962             }
963         }
964 
965         // Check if this string does not contain any uppercase characters.
966         if (lowercased) {
967             return this;
968         }
969 
970         final byte[] newValue = new byte[length()];
971         for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) {
972             newValue[i] = toLowerCase(value[j]);
973         }
974 
975         return new AsciiString(newValue, false);
976     }
977 
978     /**
979      * Converts the characters in this string to uppercase, using the default Locale.
980      *
981      * @return a new string containing the uppercase characters equivalent to the characters in this string.
982      */
983     public AsciiString toUpperCase() {
984         boolean uppercased = true;
985         int i, j;
986         final int len = length() + arrayOffset();
987         for (i = arrayOffset(); i < len; ++i) {
988             byte b = value[i];
989             if (b >= 'a' && b <= 'z') {
990                 uppercased = false;
991                 break;
992             }
993         }
994 
995         // Check if this string does not contain any lowercase characters.
996         if (uppercased) {
997             return this;
998         }
999 
1000         final byte[] newValue = new byte[length()];
1001         for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) {
1002             newValue[i] = toUpperCase(value[j]);
1003         }
1004 
1005         return new AsciiString(newValue, false);
1006     }
1007 
1008     /**
1009      * Copies this string removing white space characters from the beginning and end of the string, and tries not to
1010      * copy if possible.
1011      *
1012      * @param c The {@link CharSequence} to trim.
1013      * @return a new string with characters {@code <= \\u0020} removed from the beginning and the end.
1014      */
1015     public static CharSequence trim(CharSequence c) {
1016         if (c.getClass() == AsciiString.class) {
1017             return ((AsciiString) c).trim();
1018         }
1019         if (c instanceof String) {
1020             return ((String) c).trim();
1021         }
1022         int start = 0, last = c.length() - 1;
1023         int end = last;
1024         while (start <= end && c.charAt(start) <= ' ') {
1025             start++;
1026         }
1027         while (end >= start && c.charAt(end) <= ' ') {
1028             end--;
1029         }
1030         if (start == 0 && end == last) {
1031             return c;
1032         }
1033         return c.subSequence(start, end);
1034     }
1035 
1036     /**
1037      * Duplicates this string removing white space characters from the beginning and end of the
1038      * string, without copying.
1039      *
1040      * @return a new string with characters {@code <= \\u0020} removed from the beginning and the end.
1041      */
1042     public AsciiString trim() {
1043         int start = arrayOffset(), last = arrayOffset() + length() - 1;
1044         int end = last;
1045         while (start <= end && value[start] <= ' ') {
1046             start++;
1047         }
1048         while (end >= start && value[end] <= ' ') {
1049             end--;
1050         }
1051         if (start == 0 && end == last) {
1052             return this;
1053         }
1054         return new AsciiString(value, start, end - start + 1, false);
1055     }
1056 
1057     /**
1058      * Compares a {@code CharSequence} to this {@code String} to determine if their contents are equal.
1059      *
1060      * @param a the character sequence to compare to.
1061      * @return {@code true} if equal, otherwise {@code false}
1062      */
1063     public boolean contentEquals(CharSequence a) {
1064         if (a == null || a.length() != length()) {
1065             return false;
1066         }
1067         if (a.getClass() == AsciiString.class) {
1068             return equals(a);
1069         }
1070 
1071         for (int i = arrayOffset(), j = 0; j < a.length(); ++i, ++j) {
1072             if (b2c(value[i]) != a.charAt(j)) {
1073                 return false;
1074             }
1075         }
1076         return true;
1077     }
1078 
1079     /**
1080      * Determines whether this string matches a given regular expression.
1081      *
1082      * @param expr the regular expression to be matched.
1083      * @return {@code true} if the expression matches, otherwise {@code false}.
1084      * @throws PatternSyntaxException if the syntax of the supplied regular expression is not valid.
1085      * @throws NullPointerException if {@code expr} is {@code null}.
1086      */
1087     public boolean matches(String expr) {
1088         return Pattern.matches(expr, this);
1089     }
1090 
1091     /**
1092      * Splits this string using the supplied regular expression {@code expr}. The parameter {@code max} controls the
1093      * behavior how many times the pattern is applied to the string.
1094      *
1095      * @param expr the regular expression used to divide the string.
1096      * @param max the number of entries in the resulting array.
1097      * @return an array of Strings created by separating the string along matches of the regular expression.
1098      * @throws NullPointerException if {@code expr} is {@code null}.
1099      * @throws PatternSyntaxException if the syntax of the supplied regular expression is not valid.
1100      * @see Pattern#split(CharSequence, int)
1101      */
1102     public AsciiString[] split(String expr, int max) {
1103         return toAsciiStringArray(Pattern.compile(expr).split(this, max));
1104     }
1105 
1106     /**
1107      * Splits the specified {@link String} with the specified delimiter..
1108      */
1109     public AsciiString[] split(char delim) {
1110         final List<AsciiString> res = InternalThreadLocalMap.get().arrayList();
1111 
1112         int start = 0;
1113         final int length = length();
1114         for (int i = start; i < length; i++) {
1115             if (charAt(i) == delim) {
1116                 if (start == i) {
1117                     res.add(EMPTY_STRING);
1118                 } else {
1119                     res.add(new AsciiString(value, start + arrayOffset(), i - start, false));
1120                 }
1121                 start = i + 1;
1122             }
1123         }
1124 
1125         if (start == 0) { // If no delimiter was found in the value
1126             res.add(this);
1127         } else {
1128             if (start != length) {
1129                 // Add the last element if it's not empty.
1130                 res.add(new AsciiString(value, start + arrayOffset(), length - start, false));
1131             } else {
1132                 // Truncate trailing empty elements.
1133                 for (int i = res.size() - 1; i >= 0; i--) {
1134                     if (res.get(i).isEmpty()) {
1135                         res.remove(i);
1136                     } else {
1137                         break;
1138                     }
1139                 }
1140             }
1141         }
1142 
1143         return res.toArray(new AsciiString[res.size()]);
1144     }
1145 
1146     /**
1147      * {@inheritDoc}
1148      * <p>
1149      * Provides a case-insensitive hash code for Ascii like byte strings.
1150      */
1151     @Override
1152     public int hashCode() {
1153         int h = hash;
1154         if (h == 0) {
1155             h = PlatformDependent.hashCodeAscii(value, offset, length);
1156             hash = h;
1157         }
1158         return h;
1159     }
1160 
1161     @Override
1162     public boolean equals(Object obj) {
1163         if (obj == null || obj.getClass() != AsciiString.class) {
1164             return false;
1165         }
1166         if (this == obj) {
1167             return true;
1168         }
1169 
1170         AsciiString other = (AsciiString) obj;
1171         return length() == other.length() &&
1172                hashCode() == other.hashCode() &&
1173                PlatformDependent.equals(array(), arrayOffset(), other.array(), other.arrayOffset(), length());
1174     }
1175 
1176     /**
1177      * Translates the entire byte string to a {@link String}.
1178      * @see #toString(int)
1179      */
1180     @Override
1181     public String toString() {
1182         String cache = string;
1183         if (cache == null) {
1184             cache = toString(0);
1185             string = cache;
1186         }
1187         return cache;
1188     }
1189 
1190     /**
1191      * Translates the entire byte string to a {@link String} using the {@code charset} encoding.
1192      * @see #toString(int, int)
1193      */
1194     public String toString(int start) {
1195         return toString(start, length());
1196     }
1197 
1198     /**
1199      * Translates the [{@code start}, {@code end}) range of this byte string to a {@link String}.
1200      */
1201     public String toString(int start, int end) {
1202         int length = end - start;
1203         if (length == 0) {
1204             return "";
1205         }
1206 
1207         if (isOutOfBounds(start, length, length())) {
1208             throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
1209                             + length + ") <= srcLen(" + length() + ')');
1210         }
1211 
1212         @SuppressWarnings("deprecation")
1213         final String str = new String(value, 0, start + offset, length);
1214         return str;
1215     }
1216 
1217     public boolean parseBoolean() {
1218         return length >= 1 && value[offset] != 0;
1219     }
1220 
1221     public char parseChar() {
1222         return parseChar(0);
1223     }
1224 
1225     public char parseChar(int start) {
1226         if (start + 1 >= length()) {
1227             throw new IndexOutOfBoundsException("2 bytes required to convert to character. index " +
1228                     start + " would go out of bounds.");
1229         }
1230         final int startWithOffset = start + offset;
1231         return (char) ((b2c(value[startWithOffset]) << 8) | b2c(value[startWithOffset + 1]));
1232     }
1233 
1234     public short parseShort() {
1235         return parseShort(0, length(), 10);
1236     }
1237 
1238     public short parseShort(int radix) {
1239         return parseShort(0, length(), radix);
1240     }
1241 
1242     public short parseShort(int start, int end) {
1243         return parseShort(start, end, 10);
1244     }
1245 
1246     public short parseShort(int start, int end, int radix) {
1247         int intValue = parseInt(start, end, radix);
1248         short result = (short) intValue;
1249         if (result != intValue) {
1250             throw new NumberFormatException(subSequence(start, end, false).toString());
1251         }
1252         return result;
1253     }
1254 
1255     public int parseInt() {
1256         return parseInt(0, length(), 10);
1257     }
1258 
1259     public int parseInt(int radix) {
1260         return parseInt(0, length(), radix);
1261     }
1262 
1263     public int parseInt(int start, int end) {
1264         return parseInt(start, end, 10);
1265     }
1266 
1267     public int parseInt(int start, int end, int radix) {
1268         if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1269             throw new NumberFormatException();
1270         }
1271 
1272         if (start == end) {
1273             throw new NumberFormatException();
1274         }
1275 
1276         int i = start;
1277         boolean negative = byteAt(i) == '-';
1278         if (negative && ++i == end) {
1279             throw new NumberFormatException(subSequence(start, end, false).toString());
1280         }
1281 
1282         return parseInt(i, end, radix, negative);
1283     }
1284 
1285     private int parseInt(int start, int end, int radix, boolean negative) {
1286         int max = Integer.MIN_VALUE / radix;
1287         int result = 0;
1288         int currOffset = start;
1289         while (currOffset < end) {
1290             int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
1291             if (digit == -1) {
1292                 throw new NumberFormatException(subSequence(start, end, false).toString());
1293             }
1294             if (max > result) {
1295                 throw new NumberFormatException(subSequence(start, end, false).toString());
1296             }
1297             int next = result * radix - digit;
1298             if (next > result) {
1299                 throw new NumberFormatException(subSequence(start, end, false).toString());
1300             }
1301             result = next;
1302         }
1303         if (!negative) {
1304             result = -result;
1305             if (result < 0) {
1306                 throw new NumberFormatException(subSequence(start, end, false).toString());
1307             }
1308         }
1309         return result;
1310     }
1311 
1312     public long parseLong() {
1313         return parseLong(0, length(), 10);
1314     }
1315 
1316     public long parseLong(int radix) {
1317         return parseLong(0, length(), radix);
1318     }
1319 
1320     public long parseLong(int start, int end) {
1321         return parseLong(start, end, 10);
1322     }
1323 
1324     public long parseLong(int start, int end, int radix) {
1325         if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1326             throw new NumberFormatException();
1327         }
1328 
1329         if (start == end) {
1330             throw new NumberFormatException();
1331         }
1332 
1333         int i = start;
1334         boolean negative = byteAt(i) == '-';
1335         if (negative && ++i == end) {
1336             throw new NumberFormatException(subSequence(start, end, false).toString());
1337         }
1338 
1339         return parseLong(i, end, radix, negative);
1340     }
1341 
1342     private long parseLong(int start, int end, int radix, boolean negative) {
1343         long max = Long.MIN_VALUE / radix;
1344         long result = 0;
1345         int currOffset = start;
1346         while (currOffset < end) {
1347             int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
1348             if (digit == -1) {
1349                 throw new NumberFormatException(subSequence(start, end, false).toString());
1350             }
1351             if (max > result) {
1352                 throw new NumberFormatException(subSequence(start, end, false).toString());
1353             }
1354             long next = result * radix - digit;
1355             if (next > result) {
1356                 throw new NumberFormatException(subSequence(start, end, false).toString());
1357             }
1358             result = next;
1359         }
1360         if (!negative) {
1361             result = -result;
1362             if (result < 0) {
1363                 throw new NumberFormatException(subSequence(start, end, false).toString());
1364             }
1365         }
1366         return result;
1367     }
1368 
1369     public float parseFloat() {
1370         return parseFloat(0, length());
1371     }
1372 
1373     public float parseFloat(int start, int end) {
1374         return Float.parseFloat(toString(start, end));
1375     }
1376 
1377     public double parseDouble() {
1378         return parseDouble(0, length());
1379     }
1380 
1381     public double parseDouble(int start, int end) {
1382         return Double.parseDouble(toString(start, end));
1383     }
1384 
1385     public static final HashingStrategy<CharSequence> CASE_INSENSITIVE_HASHER =
1386             new HashingStrategy<CharSequence>() {
1387         @Override
1388         public int hashCode(CharSequence o) {
1389             return AsciiString.hashCode(o);
1390         }
1391 
1392         @Override
1393         public boolean equals(CharSequence a, CharSequence b) {
1394             return AsciiString.contentEqualsIgnoreCase(a, b);
1395         }
1396     };
1397 
1398     public static final HashingStrategy<CharSequence> CASE_SENSITIVE_HASHER =
1399             new HashingStrategy<CharSequence>() {
1400         @Override
1401         public int hashCode(CharSequence o) {
1402             return AsciiString.hashCode(o);
1403         }
1404 
1405         @Override
1406         public boolean equals(CharSequence a, CharSequence b) {
1407             return AsciiString.contentEquals(a, b);
1408         }
1409     };
1410 
1411     /**
1412      * Returns an {@link AsciiString} containing the given character sequence. If the given string is already a
1413      * {@link AsciiString}, just returns the same instance.
1414      */
1415     public static AsciiString of(CharSequence string) {
1416         return string.getClass() == AsciiString.class ? (AsciiString) string : new AsciiString(string);
1417     }
1418 
1419     /**
1420      * Returns an {@link AsciiString} containing the given string and retains/caches the input
1421      * string for later use in {@link #toString()}.
1422      * Used for the constants (which already stored in the JVM's string table) and in cases
1423      * where the guaranteed use of the {@link #toString()} method.
1424      */
1425     public static AsciiString cached(String string) {
1426         AsciiString asciiString = new AsciiString(string);
1427         asciiString.string = string;
1428         return asciiString;
1429     }
1430 
1431     /**
1432      * Returns the case-insensitive hash code of the specified string. Note that this method uses the same hashing
1433      * algorithm with {@link #hashCode()} so that you can put both {@link AsciiString}s and arbitrary
1434      * {@link CharSequence}s into the same headers.
1435      */
1436     public static int hashCode(CharSequence value) {
1437         if (value == null) {
1438             return 0;
1439         }
1440         if (value.getClass() == AsciiString.class) {
1441             return value.hashCode();
1442         }
1443 
1444         return PlatformDependent.hashCodeAscii(value);
1445     }
1446 
1447     /**
1448      * Determine if {@code a} contains {@code b} in a case sensitive manner.
1449      */
1450     public static boolean contains(CharSequence a, CharSequence b) {
1451         return contains(a, b, DefaultCharEqualityComparator.INSTANCE);
1452     }
1453 
1454     /**
1455      * Determine if {@code a} contains {@code b} in a case insensitive manner.
1456      */
1457     public static boolean containsIgnoreCase(CharSequence a, CharSequence b) {
1458         return contains(a, b, AsciiCaseInsensitiveCharEqualityComparator.INSTANCE);
1459     }
1460 
1461     /**
1462      * Returns {@code true} if both {@link CharSequence}'s are equals when ignore the case. This only supports 8-bit
1463      * ASCII.
1464      */
1465     public static boolean contentEqualsIgnoreCase(CharSequence a, CharSequence b) {
1466         if (a == null || b == null) {
1467             return a == b;
1468         }
1469 
1470         if (a.getClass() == AsciiString.class) {
1471             return ((AsciiString) a).contentEqualsIgnoreCase(b);
1472         }
1473         if (b.getClass() == AsciiString.class) {
1474             return ((AsciiString) b).contentEqualsIgnoreCase(a);
1475         }
1476 
1477         if (a.length() != b.length()) {
1478             return false;
1479         }
1480         for (int i = 0, j = 0; i < a.length(); ++i, ++j) {
1481             if (!equalsIgnoreCase(a.charAt(i),  b.charAt(j))) {
1482                 return false;
1483             }
1484         }
1485         return true;
1486     }
1487 
1488     /**
1489      * Determine if {@code collection} contains {@code value} and using
1490      * {@link #contentEqualsIgnoreCase(CharSequence, CharSequence)} to compare values.
1491      * @param collection The collection to look for and equivalent element as {@code value}.
1492      * @param value The value to look for in {@code collection}.
1493      * @return {@code true} if {@code collection} contains {@code value} according to
1494      * {@link #contentEqualsIgnoreCase(CharSequence, CharSequence)}. {@code false} otherwise.
1495      * @see #contentEqualsIgnoreCase(CharSequence, CharSequence)
1496      */
1497     public static boolean containsContentEqualsIgnoreCase(Collection<CharSequence> collection, CharSequence value) {
1498         for (CharSequence v : collection) {
1499             if (contentEqualsIgnoreCase(value, v)) {
1500                 return true;
1501             }
1502         }
1503         return false;
1504     }
1505 
1506     /**
1507      * Determine if {@code a} contains all of the values in {@code b} using
1508      * {@link #contentEqualsIgnoreCase(CharSequence, CharSequence)} to compare values.
1509      * @param a The collection under test.
1510      * @param b The values to test for.
1511      * @return {@code true} if {@code a} contains all of the values in {@code b} using
1512      * {@link #contentEqualsIgnoreCase(CharSequence, CharSequence)} to compare values. {@code false} otherwise.
1513      * @see #contentEqualsIgnoreCase(CharSequence, CharSequence)
1514      */
1515     public static boolean containsAllContentEqualsIgnoreCase(Collection<CharSequence> a, Collection<CharSequence> b) {
1516         for (CharSequence v : b) {
1517             if (!containsContentEqualsIgnoreCase(a, v)) {
1518                 return false;
1519             }
1520         }
1521         return true;
1522     }
1523 
1524     /**
1525      * Returns {@code true} if the content of both {@link CharSequence}'s are equals. This only supports 8-bit ASCII.
1526      */
1527     public static boolean contentEquals(CharSequence a, CharSequence b) {
1528         if (a == null || b == null) {
1529             return a == b;
1530         }
1531 
1532         if (a.getClass() == AsciiString.class) {
1533             return ((AsciiString) a).contentEquals(b);
1534         }
1535 
1536         if (b.getClass() == AsciiString.class) {
1537             return ((AsciiString) b).contentEquals(a);
1538         }
1539 
1540         if (a.length() != b.length()) {
1541             return false;
1542         }
1543         for (int i = 0; i <  a.length(); ++i) {
1544             if (a.charAt(i) != b.charAt(i)) {
1545                 return false;
1546             }
1547         }
1548         return true;
1549     }
1550 
1551     private static AsciiString[] toAsciiStringArray(String[] jdkResult) {
1552         AsciiString[] res = new AsciiString[jdkResult.length];
1553         for (int i = 0; i < jdkResult.length; i++) {
1554             res[i] = new AsciiString(jdkResult[i]);
1555         }
1556         return res;
1557     }
1558 
1559     private interface CharEqualityComparator {
1560         boolean equals(char a, char b);
1561     }
1562 
1563     private static final class DefaultCharEqualityComparator implements CharEqualityComparator {
1564         static final DefaultCharEqualityComparator INSTANCE = new DefaultCharEqualityComparator();
1565         private DefaultCharEqualityComparator() { }
1566 
1567         @Override
1568         public boolean equals(char a, char b) {
1569             return a == b;
1570         }
1571     }
1572 
1573     private static final class AsciiCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1574         static final AsciiCaseInsensitiveCharEqualityComparator
1575                 INSTANCE = new AsciiCaseInsensitiveCharEqualityComparator();
1576         private AsciiCaseInsensitiveCharEqualityComparator() { }
1577 
1578         @Override
1579         public boolean equals(char a, char b) {
1580             return equalsIgnoreCase(a, b);
1581         }
1582     }
1583 
1584     private static final class GeneralCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1585         static final GeneralCaseInsensitiveCharEqualityComparator
1586                 INSTANCE = new GeneralCaseInsensitiveCharEqualityComparator();
1587         private GeneralCaseInsensitiveCharEqualityComparator() { }
1588 
1589         @Override
1590         public boolean equals(char a, char b) {
1591             //For motivation, why we need two checks, see comment in String#regionMatches
1592             return Character.toUpperCase(a) == Character.toUpperCase(b) ||
1593                 Character.toLowerCase(a) == Character.toLowerCase(b);
1594         }
1595     }
1596 
1597     private static boolean contains(CharSequence a, CharSequence b, CharEqualityComparator cmp) {
1598         if (a == null || b == null || a.length() < b.length()) {
1599             return false;
1600         }
1601         if (b.length() == 0) {
1602             return true;
1603         }
1604         int bStart = 0;
1605         for (int i = 0; i < a.length(); ++i) {
1606             if (cmp.equals(b.charAt(bStart), a.charAt(i))) {
1607                 // If b is consumed then true.
1608                 if (++bStart == b.length()) {
1609                     return true;
1610                 }
1611             } else if (a.length() - i < b.length()) {
1612                 // If there are not enough characters left in a for b to be contained, then false.
1613                 return false;
1614             } else {
1615                 bStart = 0;
1616             }
1617         }
1618         return false;
1619     }
1620 
1621     private static boolean regionMatchesCharSequences(final CharSequence cs, final int csStart,
1622                                          final CharSequence string, final int start, final int length,
1623                                          CharEqualityComparator charEqualityComparator) {
1624         //general purpose implementation for CharSequences
1625         if (csStart < 0 || length > cs.length() - csStart) {
1626             return false;
1627         }
1628         if (start < 0 || length > string.length() - start) {
1629             return false;
1630         }
1631 
1632         int csIndex = csStart;
1633         int csEnd = csIndex + length;
1634         int stringIndex = start;
1635 
1636         while (csIndex < csEnd) {
1637             char c1 = cs.charAt(csIndex++);
1638             char c2 = string.charAt(stringIndex++);
1639 
1640             if (!charEqualityComparator.equals(c1, c2)) {
1641                 return false;
1642             }
1643         }
1644         return true;
1645     }
1646 
1647     /**
1648      * This methods make regionMatches operation correctly for any chars in strings
1649      * @param cs the {@code CharSequence} to be processed
1650      * @param ignoreCase specifies if case should be ignored.
1651      * @param csStart the starting offset in the {@code cs} CharSequence
1652      * @param string the {@code CharSequence} to compare.
1653      * @param start the starting offset in the specified {@code string}.
1654      * @param length the number of characters to compare.
1655      * @return {@code true} if the ranges of characters are equal, {@code false} otherwise.
1656      */
1657     public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int csStart,
1658                                         final CharSequence string, final int start, final int length) {
1659         if (cs == null || string == null) {
1660             return false;
1661         }
1662 
1663         if (cs instanceof String && string instanceof String) {
1664             return ((String) cs).regionMatches(ignoreCase, csStart, (String) string, start, length);
1665         }
1666 
1667         if (cs instanceof AsciiString) {
1668             return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1669         }
1670 
1671         return regionMatchesCharSequences(cs, csStart, string, start, length,
1672                                             ignoreCase ? GeneralCaseInsensitiveCharEqualityComparator.INSTANCE :
1673                                                     DefaultCharEqualityComparator.INSTANCE);
1674     }
1675 
1676     /**
1677      * This is optimized version of regionMatches for string with ASCII chars only
1678      * @param cs the {@code CharSequence} to be processed
1679      * @param ignoreCase specifies if case should be ignored.
1680      * @param csStart the starting offset in the {@code cs} CharSequence
1681      * @param string the {@code CharSequence} to compare.
1682      * @param start the starting offset in the specified {@code string}.
1683      * @param length the number of characters to compare.
1684      * @return {@code true} if the ranges of characters are equal, {@code false} otherwise.
1685      */
1686     public static boolean regionMatchesAscii(final CharSequence cs, final boolean ignoreCase, final int csStart,
1687                                         final CharSequence string, final int start, final int length) {
1688         if (cs == null || string == null) {
1689             return false;
1690         }
1691 
1692         if (!ignoreCase && cs instanceof String && string instanceof String) {
1693             //we don't call regionMatches from String for ignoreCase==true. It's a general purpose method,
1694             //which make complex comparison in case of ignoreCase==true, which is useless for ASCII-only strings.
1695             //To avoid applying this complex ignore-case comparison, we will use regionMatchesCharSequences
1696             return ((String) cs).regionMatches(false, csStart, (String) string, start, length);
1697         }
1698 
1699         if (cs instanceof AsciiString) {
1700             return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1701         }
1702 
1703         return regionMatchesCharSequences(cs, csStart, string, start, length,
1704                                           ignoreCase ? AsciiCaseInsensitiveCharEqualityComparator.INSTANCE :
1705                                                       DefaultCharEqualityComparator.INSTANCE);
1706     }
1707 
1708     /**
1709      * <p>Case in-sensitive find of the first index within a CharSequence
1710      * from the specified position.</p>
1711      *
1712      * <p>A {@code null} CharSequence will return {@code -1}.
1713      * A negative start position is treated as zero.
1714      * An empty ("") search CharSequence always matches.
1715      * A start position greater than the string length only matches
1716      * an empty search CharSequence.</p>
1717      *
1718      * <pre>
1719      * AsciiString.indexOfIgnoreCase(null, *, *)          = -1
1720      * AsciiString.indexOfIgnoreCase(*, null, *)          = -1
1721      * AsciiString.indexOfIgnoreCase("", "", 0)           = 0
1722      * AsciiString.indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
1723      * AsciiString.indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
1724      * AsciiString.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
1725      * AsciiString.indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
1726      * AsciiString.indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
1727      * AsciiString.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
1728      * AsciiString.indexOfIgnoreCase("aabaabaa", "", 2)   = 2
1729      * AsciiString.indexOfIgnoreCase("abc", "", 9)        = -1
1730      * </pre>
1731      *
1732      * @param str  the CharSequence to check, may be null
1733      * @param searchStr  the CharSequence to find, may be null
1734      * @param startPos  the start position, negative treated as zero
1735      * @return the first index of the search CharSequence (always &ge; startPos),
1736      *  -1 if no match or {@code null} string input
1737      */
1738     public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) {
1739         if (str == null || searchStr == null) {
1740             return INDEX_NOT_FOUND;
1741         }
1742         if (startPos < 0) {
1743             startPos = 0;
1744         }
1745         int searchStrLen = searchStr.length();
1746         final int endLimit = str.length() - searchStrLen + 1;
1747         if (startPos > endLimit) {
1748             return INDEX_NOT_FOUND;
1749         }
1750         if (searchStrLen == 0) {
1751             return startPos;
1752         }
1753         for (int i = startPos; i < endLimit; i++) {
1754             if (regionMatches(str, true, i, searchStr, 0, searchStrLen)) {
1755                 return i;
1756             }
1757         }
1758         return INDEX_NOT_FOUND;
1759     }
1760 
1761     /**
1762      * <p>Case in-sensitive find of the first index within a CharSequence
1763      * from the specified position. This method optimized and works correctly for ASCII CharSequences only</p>
1764      *
1765      * <p>A {@code null} CharSequence will return {@code -1}.
1766      * A negative start position is treated as zero.
1767      * An empty ("") search CharSequence always matches.
1768      * A start position greater than the string length only matches
1769      * an empty search CharSequence.</p>
1770      *
1771      * <pre>
1772      * AsciiString.indexOfIgnoreCase(null, *, *)          = -1
1773      * AsciiString.indexOfIgnoreCase(*, null, *)          = -1
1774      * AsciiString.indexOfIgnoreCase("", "", 0)           = 0
1775      * AsciiString.indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
1776      * AsciiString.indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
1777      * AsciiString.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
1778      * AsciiString.indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
1779      * AsciiString.indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
1780      * AsciiString.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
1781      * AsciiString.indexOfIgnoreCase("aabaabaa", "", 2)   = 2
1782      * AsciiString.indexOfIgnoreCase("abc", "", 9)        = -1
1783      * </pre>
1784      *
1785      * @param str  the CharSequence to check, may be null
1786      * @param searchStr  the CharSequence to find, may be null
1787      * @param startPos  the start position, negative treated as zero
1788      * @return the first index of the search CharSequence (always &ge; startPos),
1789      *  -1 if no match or {@code null} string input
1790      */
1791     public static int indexOfIgnoreCaseAscii(final CharSequence str, final CharSequence searchStr, int startPos) {
1792         if (str == null || searchStr == null) {
1793             return INDEX_NOT_FOUND;
1794         }
1795         if (startPos < 0) {
1796             startPos = 0;
1797         }
1798         int searchStrLen = searchStr.length();
1799         final int endLimit = str.length() - searchStrLen + 1;
1800         if (startPos > endLimit) {
1801             return INDEX_NOT_FOUND;
1802         }
1803         if (searchStrLen == 0) {
1804             return startPos;
1805         }
1806         for (int i = startPos; i < endLimit; i++) {
1807             if (regionMatchesAscii(str, true, i, searchStr, 0, searchStrLen)) {
1808                 return i;
1809             }
1810         }
1811         return INDEX_NOT_FOUND;
1812     }
1813 
1814     /**
1815      * <p>Finds the first index in the {@code CharSequence} that matches the
1816      * specified character.</p>
1817      *
1818      * @param cs  the {@code CharSequence} to be processed, not null
1819      * @param searchChar the char to be searched for
1820      * @param start  the start index, negative starts at the string start
1821      * @return the index where the search char was found,
1822      * -1 if char {@code searchChar} is not found or {@code cs == null}
1823      */
1824     //-----------------------------------------------------------------------
1825     public static int indexOf(final CharSequence cs, final char searchChar, int start) {
1826         if (cs instanceof String) {
1827             return ((String) cs).indexOf(searchChar, start);
1828         } else if (cs instanceof AsciiString) {
1829             return ((AsciiString) cs).indexOf(searchChar, start);
1830         }
1831         if (cs == null) {
1832             return INDEX_NOT_FOUND;
1833         }
1834         final int sz = cs.length();
1835         if (start < 0) {
1836             start = 0;
1837         }
1838         for (int i = start; i < sz; i++) {
1839             if (cs.charAt(i) == searchChar) {
1840                 return i;
1841             }
1842         }
1843         return INDEX_NOT_FOUND;
1844     }
1845 
1846     private static boolean equalsIgnoreCase(byte a, byte b) {
1847         return a == b || toLowerCase(a) == toLowerCase(b);
1848     }
1849 
1850     private static boolean equalsIgnoreCase(char a, char b) {
1851         return a == b || toLowerCase(a) == toLowerCase(b);
1852     }
1853 
1854     private static byte toLowerCase(byte b) {
1855         return isUpperCase(b) ? (byte) (b + 32) : b;
1856     }
1857 
1858     private static char toLowerCase(char c) {
1859         return isUpperCase(c) ? (char) (c + 32) : c;
1860     }
1861 
1862     private static byte toUpperCase(byte b) {
1863         return isLowerCase(b) ? (byte) (b - 32) : b;
1864     }
1865 
1866     private static boolean isLowerCase(byte value) {
1867         return value >= 'a' && value <= 'z';
1868     }
1869 
1870     public static boolean isUpperCase(byte value) {
1871         return value >= 'A' && value <= 'Z';
1872     }
1873 
1874     public static boolean isUpperCase(char value) {
1875         return value >= 'A' && value <= 'Z';
1876     }
1877 
1878     public static byte c2b(char c) {
1879         return (byte) ((c > MAX_CHAR_VALUE) ? '?' : c);
1880     }
1881 
1882     public static char b2c(byte b) {
1883         return (char) (b & 0xFF);
1884     }
1885 }