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