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