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