Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.osgl.util;
/*-
* #%L
* Java Tool
* %%
* Copyright (C) 2014 - 2017 OSGL (Open Source General Library)
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.osgl.$;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.lang.Character.*;
/**
* FastStr implements the same contract of Str with the a char array.
* This class is marked as Fast because of the following points:
* 1. When unsafeOf(String) is called, it share the internal value (the char array) of the
* String been passed in
* 2. when unsafeOf(char[]) is called, it use the char array passed in directly without
* copy operation
* 3. subList and substring works at O(1) because it will NOT copy the internal char array
* Note, this class shall be used with caution as it might prevent a very large char array
* from been garbage collected when the char array is passed to a FastStr or substr of
* the fast string
*/
public class FastStr extends StrBase
implements RandomAccess, CharSequence, java.io.Serializable, Comparable {
public static final FastStr EMPTY_STR = new FastStr() {
@Override
public String toString() {
return "";
}
};
@Override
protected Class _impl() {
return FastStr.class;
}
@Override
protected FastStr _empty() {
return EMPTY_STR;
}
private final char[] buf;
// low end point inclusive
private final int begin;
// high end point exclusive
private final int end;
private int hash;
private FastStr() {
buf = new char[0];
begin = 0;
end = 0;
}
private FastStr(char[] buf) {
this(buf, 0, buf.length);
}
private FastStr(char[] buf, int start, int end) {
this.buf = buf;
this.begin = start;
this.end = end;
}
@Override
public int length() {
return end - begin;
}
@Override
public boolean isEmpty() {
return EMPTY_STR == this || buf.length == 0 || end <= begin ;
}
@Override
public boolean isBlank() {
if (isEmpty()) {
return true;
}
for (int i = begin; i < end; ++i) {
char c = buf[i];
if (c > ' ') {
return false;
}
}
return true;
}
@Override
public FastStr subList(int fromIndex, int toIndex) {
if (fromIndex < 0) {
throw new StringIndexOutOfBoundsException(fromIndex);
}
int len = size();
if (toIndex > len) {
throw new StringIndexOutOfBoundsException(toIndex);
}
int subLen = toIndex - fromIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
if (fromIndex == toIndex) {
return EMPTY_STR;
}
int newFrom = toInternalId(fromIndex);
int newTo = toInternalId(toIndex);
return new FastStr(buf, newFrom, newTo);
}
@Override
public FastStr takeWhile($.Function super Character, Boolean> predicate) {
if (isEmpty()) {
return EMPTY_STR;
}
int sz = size(), b = toInternalId(0), e = -1;
for (int i = 0; i < sz; ++i) {
char c = charAt(i);
e = toInternalId(i);
if (!predicate.apply(c)) break;
}
return unsafeOf(buf, b, e);
}
@Override
public FastStr dropWhile($.Function super Character, Boolean> predicate) {
int sz = size();
if (sz == 0) return EMPTY_STR;
int b = -1, e = toInternalId(sz);
for (int i = 0; i < sz; ++i) {
char c = charAt(i);
b = toInternalId(i);
if (!predicate.apply(c)) break;
}
return unsafeOf(buf, b, e);
}
@Override
public FastStr remove($.Function super Character, Boolean> predicate) {
final int sz = size();
if (sz == 0) return EMPTY_STR;
char[] newBuf = null;
boolean removed = false;
int curNew = 0;
for (int i = 0; i < sz; ++i) {
char c = charAt(i);
if (predicate.apply(c)) {
if (null == newBuf) {
removed = true;
newBuf = new char[sz];
System.arraycopy(buf, 0, newBuf, 0, i);
}
} else {
if (null != newBuf) {
newBuf[curNew] = c;
}
curNew++;
}
}
if (!removed) {
// nothing removed
return this;
}
return unsafeOf(newBuf, 0, curNew);
}
@Override
public FastStr insert(int index, char character) throws StringIndexOutOfBoundsException {
E.NPE(character);
int len = size();
if (len < Math.abs(index)) {
throw new StringIndexOutOfBoundsException(index);
}
if (index < 0) {
index = len + index;
}
char[] newBuf = new char[len + 1];
if (index > 0) {
System.arraycopy(buf, begin, newBuf, 0, index);
}
if (index < len) {
System.arraycopy(buf, begin + index, newBuf, index + 1, len - index);
}
newBuf[index] = character;
return unsafeOf(newBuf, 0, len + 1);
}
@Override
public FastStr insert(int index, Character character) throws StringIndexOutOfBoundsException {
E.NPE(character);
int len = size();
if (len < Math.abs(index)) {
throw new StringIndexOutOfBoundsException(index);
}
if (index < 0) {
index = len + index;
}
char[] newBuf = new char[len + 1];
if (index > 0) {
System.arraycopy(buf, begin, newBuf, 0, index);
}
if (index < len) {
System.arraycopy(buf, begin + index, newBuf, index + 1, len - index);
}
newBuf[index] = character;
return unsafeOf(newBuf, 0, len + 1);
}
@Override
public FastStr insert(int index, StrBase> str) throws StringIndexOutOfBoundsException {
return insert(index, str.toCharArray());
}
@Override
public FastStr insert(int index, Character... ca) throws StringIndexOutOfBoundsException {
return insert(0, $.asPrimitive(ca));
}
@Override
public FastStr insert(int index, char... ca) throws StringIndexOutOfBoundsException {
int delta = ca.length;
if (delta == 0) {
return this;
}
int len = size();
if (len < Math.abs(index)) {
throw new StringIndexOutOfBoundsException(index);
}
if (index < 0) {
index = len + index;
}
char[] newBuf = new char[len + delta];
if (index > 0) {
System.arraycopy(buf, begin, newBuf, 0, index);
}
System.arraycopy(ca, 0, newBuf, index, delta);
if (index < len) {
System.arraycopy(buf, begin + index, newBuf, index + delta, len - index);
}
return unsafeOf(newBuf, 0, len + delta);
}
@Override
public FastStr insert(int index, String s) throws StringIndexOutOfBoundsException {
return insert(index, s.toCharArray());
}
@Override
public FastStr reverse() {
int sz = size();
char[] newBuf = new char[sz];
for (int i = 0, j = sz - 1; i < sz; ) {
newBuf[j--] = buf[toInternalId(i++)];
}
return new FastStr(newBuf, 0, sz);
}
@Override
@SuppressWarnings("unchecked")
public FastStr append(Collection extends Character> collection) {
int sz = size(), sz2 = collection.size();
if (0 == sz2) return this;
if (0 == sz) return of((Collection) collection);
char[] newBuf = new char[sz + sz2];
copyTo(newBuf, 0);
int i = sz;
Iterator extends Character> itr = collection.iterator();
while (itr.hasNext()) {
char c = itr.next();
newBuf[i++] = c;
}
return new FastStr(newBuf, 0, sz + sz2);
}
@Override
public FastStr append(C.List list) {
int sz = size(), sz2 = list.size();
if (0 == sz2) return this;
if (0 == sz) return of(list);
char[] newBuf = new char[sz + sz2];
copyTo(newBuf, 0);
for (int i = 0; i < sz2; ++i) {
newBuf[sz + i] = list.get(i);
}
return new FastStr(newBuf, 0, sz + sz2);
}
@Override
public FastStr parallel() {
return super.parallel();
}
@Override
public FastStr append(char... array) {
int sz = size(), sz2 = array.length;
if (0 == sz2) return this;
if (0 == sz) return of(array);
char[] newBuf = new char[sz + sz2];
copyTo(newBuf, 0);
for (int i = 0; i < sz2; ++i) {
newBuf[sz + i] = array[i];
}
return new FastStr(newBuf, 0, sz + sz2);
}
@Override
public FastStr append(Character character) {
int sz = size();
char[] newBuf = new char[sz + 1];
copyTo(newBuf, 0);
newBuf[sz] = character;
return new FastStr(newBuf, 0, sz + 1);
}
public FastStr append(FastStr s) {
int sz = size(), sz2 = s.size();
if (sz == 0) return s;
if (sz2 == 0) return this;
int newSz = sz + sz2;
char[] newBuf = new char[newSz];
copyTo(newBuf, 0);
s.copyTo(newBuf, sz);
return new FastStr(newBuf, 0, newSz);
}
public FastStr append(String s) {
int sz = size(), sz2 = s.length();
if (0 == sz) return of(s);
if (0 == sz2) return this;
int newSz = sz + sz2;
char[] newBuf = new char[newSz];
copyTo(newBuf, 0);
boolean done = false;
if (sz2 > 512) {
try {
char[] sBuf = s.toCharArray();
System.arraycopy(sBuf, 0, newBuf, sz, sz2);
done = true;
} catch (RuntimeException e) {
// ignore
}
}
if (!done) {
for (int i = 0; i < sz2; ++i) {
newBuf[sz + i] = s.charAt(i);
}
}
return new FastStr(newBuf, 0, newSz);
}
@Override
@SuppressWarnings("unchecked")
public FastStr prepend(Collection extends Character> collection) {
int sz = size(), sz2 = collection.size();
if (0 == sz2) return this;
if (0 == sz) return of((Collection) collection);
int newSz = sz + sz2, i = 0;
char[] newBuf = new char[newSz];
Iterator extends Character> itr = collection.iterator();
while (itr.hasNext()) {
newBuf[i++] = itr.next();
}
copyTo(newBuf, sz2);
return new FastStr(newBuf, 0, newSz);
}
@Override
public FastStr prepend(C.List list) {
int sz = size();
if (0 == sz) return of(list);
int sz2 = list.size();
if (0 == sz2) return this;
if (1 == sz2) {
return prepend(list.get(0));
}
int newSz = sz + sz2;
char[] newBuf = new char[newSz];
for (int i = 0; i < sz2; ++i) {
newBuf[i] = list.get(i);
}
copyTo(newBuf, sz2);
return new FastStr(newBuf, 0, newSz);
}
@Override
public FastStr prepend(char... chars) {
int sz = size();
if (0 == sz) return of(chars);
int sz2 = chars.length;
if (0 == sz2) return this;
if (1 == sz2) {
return prepend(chars[0]);
}
int newSz = sz + sz2;
char[] newBuf = new char[newSz];
for (int i = 0; i < sz2; ++i) {
newBuf[i] = chars[i];
}
copyTo(newBuf, sz2);
return new FastStr(newBuf, 0, newSz);
}
@Override
public FastStr prepend(Character character) {
// check if I can back begin pointer for one step
if (begin > 0) {
if (buf[begin - 1] == character) {
return FastStr.unsafeOf(buf, begin - 1, end);
}
}
int sz = size();
char[] newBuf = new char[++sz];
newBuf[0] = character;
copyTo(newBuf, 1);
return new FastStr(newBuf, 0, sz);
}
@Override
public FastStr prepend(FastStr s) {
return s.append(this);
}
@Override
public FastStr prepend(String s) {
int sz = size();
if (0 == sz) return of(s);
int sz2 = s.length();
if (0 == sz2) return this;
if (sz2 == 1) {
return prepend(s.charAt(0));
}
int newSz = sz + sz2;
char[] newBuf = new char[newSz];
boolean done = false;
if (sz2 > 512) {
try {
char[] sBuf = s.toCharArray();
System.arraycopy(sBuf, 0, newBuf, 0, sz2);
done = true;
} catch (RuntimeException e) {
// ignore
}
}
if (!done) {
for (int i = 0; i < sz2; ++i) {
newBuf[i] = s.charAt(i);
}
}
copyTo(newBuf, sz2);
return new FastStr(newBuf, 0, newSz);
}
@Override
public FastStr padLeft(char c, int times) {
char[] ca = new char[times];
$.fill(c, ca);
return prepend(ca);
}
@Override
public FastStr lpad(char c, int times) {
return padLeft(c, times);
}
@Override
public FastStr padLeft(int times) {
return padLeft(' ', times);
}
@Override
public FastStr lpad(int times) {
return padLeft(times);
}
@Override
public FastStr padRight(char c, int times) {
char[] ca = new char[times];
$.fill(c, ca);
return append(ca);
}
@Override
public FastStr rpad(char c, int times) {
return padRight(c, times);
}
@Override
public FastStr padRight(int times) {
return padRight(' ', times);
}
@Override
public FastStr rpad(int times) {
return padRight(times);
}
@Override
public char charAt(int index) {
return buf[toInternalId(index)];
}
@Override
public FastStr subSequence(int start, int end) {
return subList(start, end);
}
@Override
public FastStr copy() {
if (EMPTY_STR == this) return this;
return unsafeOf(charArray(), 0, size());
}
public FastStr times(int n) {
E.illegalArgumentIf(n < 0, "n cannot be negative");
if (0 == n) return EMPTY_STR;
if (1 == n) return this;
int sz = size();
if (0 == sz) return this;
int newSz = sz * n;
char[] newBuf = new char[newSz];
for (int i = 0; i < n; ++i) {
copyTo(newBuf, i * sz);
}
return new FastStr(newBuf, 0, newSz);
}
public FastStr canonical() {
if (EMPTY_STR == this) return this;
if (begin == 0) return this;
return unsafeOf(unsafeChars(), 0, size());
}
@Override
public int compareTo(FastStr o) {
int len1 = size();
int len2 = o.size();
int lim = Math.min(len1, len2);
char v1[] = buf;
char v2[] = o.buf;
int k = 0;
while (k < lim) {
char c1 = v1[toInternalId(k)];
char c2 = v2[o.toInternalId(k)];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
@Override
public String toString() {
char[] newBuf = charArray();
return new String(newBuf);
}
public FastStr toFastStr() {
return this;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o instanceof FastStr) {
FastStr that = (FastStr) o;
return contentEquals(that);
} else if (o instanceof StrBase) {
StrBase that = (StrBase)o;
return that.contentEquals(this);
}
return false;
}
@Override
public int hashCode() {
if (isEmpty()) return 0;
int h = hash;
if (h == 0) {
for (int i = begin; i < end; ++i) {
h = 31 * h + buf[i];
}
hash = h;
}
return h;
}
// --- String utilities ---
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
int sz = size();
if (srcEnd > sz) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
System.arraycopy(buf, toInternalId(srcBegin), dst, dstBegin, srcEnd - srcBegin);
}
public byte[] getBytes() {
String s = toString();
return s.getBytes();
}
public byte[] getBytes(String charsetName) {
String s = toString();
if (null == charsetName) {
return s.getBytes();
}
try {
return s.getBytes(charsetName);
} catch (UnsupportedEncodingException e) {
throw E.encodingException(e);
}
}
@Override
public byte[] getBytes(Charset charset) {
String s = toString();
if (null == charset) {
return s.getBytes();
}
return s.getBytes(charset);
}
public byte[] getBytesAscII() {
int sz = size();
if (sz == 0) {
return new byte[0];
}
return toString().getBytes(Charsets.US_ASCII);
}
@Override
public byte[] getBytesUTF8() {
int sz = size();
if (sz == 0) {
return new byte[0];
}
return toString().getBytes(Charsets.UTF_8);
}
/**
* Wrapper of {@link String#contentEquals(CharSequence)}
*
* @param x the char sequence to be compared
* @return true if content equals content of the specified char sequence
*/
public boolean contentEquals(CharSequence x) {
if (x == this) return true;
if (isEmpty()) return x.length() == 0;
int sz = size(), sz2 = x.length();
if (sz != sz2) return false;
for (int i = 0; i < sz; ++i) {
char c = buf[toInternalId(i)];
char c1 = x.charAt(i);
if (c != c1) return false;
}
return true;
}
public boolean contentEquals(FastStr x) {
if (x == this) return true;
if (null == x) return false;
int sz = size(), sz2 = x.size();
if (sz != sz2) return false;
for (int i = begin, j = x.begin; i < end; ) {
if (buf[i++] != x.buf[j++]) return false;
}
return true;
}
/**
* Wrapper of {@link String#equalsIgnoreCase(String)}
*
* @param x the char sequence to be compared
* @return {@code true} if the argument is not {@code null} and it
* represents an equivalent {@code String} ignoring case; {@code
* false} otherwise
*/
public boolean equalsIgnoreCase(CharSequence x) {
if (x == this) return true;
if (null == x || size() != x.length()) return false;
if (isEmpty() && x.length() == 0) return true;
return regionMatches(true, 0, x, 0, size());
}
public boolean equalsIgnoreCase(FastStr x) {
if (x == this) return true;
if (null == x || size() != x.size()) return false;
return regionMatches(true, 0, x.buf, 0, size());
}
public int compareTo(CharSequence x) {
int len1 = size();
int len2 = x.length();
int lim = Math.min(len1, len2);
char v1[] = buf;
try {
char v2[] = FastStr.bufOf(x);
int k = 0;
while (k < lim) {
char c1 = v1[toInternalId(k)];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
} catch (RuntimeException e) {
int k = 0;
while (k < lim) {
char c1 = v1[toInternalId(k)];
char c2 = x.charAt(k);
if (c1 != c2) {
return c1 - c2;
}
k++;
}
}
return len1 - len2;
}
public int compareToIgnoreCase(FastStr o) {
int len1 = size();
int len2 = o.size();
int lim = Math.min(len1, len2);
char v1[] = buf;
char v2[] = o.buf;
int k = 0;
while (k < lim) {
char c1 = v1[toInternalId(k)];
char c2 = v2[o.toInternalId(k)];
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
return c1 - c2;
}
}
}
k++;
}
return len1 - len2;
}
public int compareToIgnoreCase(CharSequence o) {
int len1 = size();
int len2 = o.length();
int lim = Math.min(len1, len2);
char v1[] = buf;
int k = 0;
try {
char v2[] = FastStr.of(o).unsafeChars();
while (k < lim) {
char c1 = v1[toInternalId(k)];
char c2 = v2[k];
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
return c1 - c2;
}
}
}
k++;
}
} catch (RuntimeException e) {
while (k < lim) {
char c1 = v1[toInternalId(k)];
char c2 = o.charAt(k);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
return c1 - c2;
}
}
}
k++;
}
}
return len1 - len2;
}
public boolean regionMatches(boolean ignoreCase, int toffset,
FastStr other, int ooffset, int len
) {
return regionMatches(ignoreCase, toffset, other.unsafeChars(), ooffset, len);
}
private boolean regionMatches(boolean ignoreCase, int toffset, char[] other, int ooffset, int len) {
char ta[] = buf;
int to = toInternalId(toffset);
char pa[] = other;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (to < 0)
|| (toffset > (long) length() - len)
|| (ooffset > (long) other.length - len)) {
return false;
}
while (len-- > 0) {
char c1 = ta[to++];
char c2 = pa[po++];
if (c1 == c2) {
continue;
}
if (ignoreCase) {
// If characters don't match but case may be ignored,
// try converting both characters to uppercase.
// If the results match, then the comparison scan should
// continue.
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
// Unfortunately, conversion to uppercase does not work properly
// for the Georgian alphabet, which has strange rules about case
// conversion. So we need to make one last check before
// exiting.
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
}
public boolean regionMatches(boolean ignoreCase, int toffset,
CharSequence other, int ooffset, int len
) {
char[] otherBuf = bufOf(other);
return regionMatches(ignoreCase, toffset, otherBuf, ooffset, len);
}
public boolean startsWith(FastStr prefix, int toffset) {
if (prefix.isEmpty()) return true;
int sz2 = prefix.size(), sz = size();
if (toffset < 0 || toffset > sz - sz2) {
return false;
}
int po = 0, pc = sz2, to = toffset;
char[] buf1 = buf, buf2 = prefix.buf;
while (--pc >= 0) {
if (buf1[toInternalId(to++)] != buf2[prefix.toInternalId(po++)]) {
return false;
}
}
return true;
}
public boolean startsWith(CharSequence suffix, int toffset) {
if (suffix.length() == 0) return true;
int sz2 = suffix.length(), sz = size();
if (toffset < 0 || toffset > sz - sz2) {
return false;
}
int po = 0, pc = sz2, to = toffset;
char[] buf1 = buf;
try {
char[] buf2 = FastStr.bufOf(suffix);
while (--pc >= 0) {
if (buf1[toInternalId(to++)] != buf2[po++]) {
return false;
}
}
return true;
} catch (RuntimeException e) {
while (--pc >= 0) {
if (buf1[toInternalId(to++)] != suffix.charAt(po++)) {
return false;
}
}
return true;
}
}
public boolean endsWith(CharSequence suffix, int toffset) {
int prefixSz = suffix.length();
if (0 == prefixSz) {
return true;
}
int matchStart = length() - toffset;
if (matchStart < prefixSz) {
return false;
}
for (int i = toInternalId(matchStart - 1), j = prefixSz - 1; j >= 0; --i, --j) {
char c0 = buf[i];
char c1 = suffix.charAt(j);
if (c0 != c1) {
return false;
}
}
return true;
}
@Override
public boolean endsWith(FastStr prefix, int toffset) {
int prefixSz = prefix.length();
if (0 == prefixSz) {
return true;
}
int matchStart = length() - toffset;
if (matchStart < prefixSz) {
return false;
}
char[] prefixBuf = prefix.buf;
for (int i = toInternalId(matchStart - 1), j = prefix.toInternalId(prefixSz - 1); j >= prefix.begin; --i, --j) {
char c0 = buf[i];
char c1 = prefixBuf[j];
if (c0 != c1) {
return false;
}
}
return true;
}
public int indexOf(int ch, int fromIndex) {
final int max = size();
if (fromIndex < 0) {
fromIndex = 0;
} else if (fromIndex > max) {
return -1;
}
fromIndex = toInternalId(fromIndex);
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
final char[] buf = this.buf;
for (int i = fromIndex; i < end; ++i) {
if (buf[i] == ch) {
return toExternalId(i);
}
}
return -1;
} else {
return toExternalId(indexOfSupplementary(ch, fromIndex));
}
}
public int lastIndexOf(int ch, int fromIndex) {
fromIndex = toInternalId(fromIndex);
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
final char[] value = this.buf;
int i = Math.min(fromIndex, length() - 1 + begin);
for (; i >= begin; i--) {
if (value[i] == ch) {
return toExternalId(i);
}
}
return -1;
} else {
return toExternalId(lastIndexOfSupplementary(ch, fromIndex));
}
}
@Override
public int indexOf(CharSequence str, int fromIndex) {
char[] strBuf = bufOf(str);
return S.indexOf(buf, begin, size(), strBuf, 0, strBuf.length, fromIndex);
}
@Override
public int indexOf(FastStr str, int fromIndex) {
char[] buf = str.buf;
return S.indexOf(this.buf, this.begin, this.size(), buf, str.begin, str.size(), fromIndex);
}
@Override
public int lastIndexOf(CharSequence str, int fromIndex) {
char[] strBuf = bufOf(str);
int sz = size();
return S.lastIndexOf(buf, begin, sz, strBuf, 0, strBuf.length, fromIndex);
}
@Override
public int lastIndexOf(FastStr str, int fromIndex) {
int sz = size();
return S.lastIndexOf(buf, begin, sz, str.buf, str.begin, str.size(), fromIndex);
}
/**
* Wrapper of {@link String#substring(int)}
*
* @param beginIndex the begin index
* @return a String instance that is equivalent to a sub part of this FastStr
*/
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = size() - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? toString() : new String(buf, toInternalId(beginIndex), subLen);
}
/**
* Wrapper of {@link String#substring(int, int)}
*
* @param beginIndex begin index
* @param endIndex end index
* @return a String instance that is equivalent to sub part of this FastStr
*/
@Override
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
char[] buf = this.buf;
int sz = buf.length;
if (endIndex > sz) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == sz)) ? toString()
: new String(buf, toInternalId(beginIndex), subLen);
}
/**
* Wrapper of {@link String#replace(char, char)} but return FastStr instance
*
* @param oldChar char to be replaced
* @param newChar char used to replace {@code oldChar}
* @return a FastStr instance with all {@code oldChar} been replaced with {@code newChar}
*/
@Override
public FastStr replace(char oldChar, char newChar) {
if (oldChar != newChar) {
char[] val = this.buf; /* avoid getfield opcode */
int len = length();
int i = begin - 1;
while (++i < end) {
if (val[i] == oldChar) {
break;
}
}
if (i < end) {
char buf[] = new char[len];
for (int j = begin; j < i; j++) {
buf[j - begin] = val[j];
}
while (i < end) {
char c = val[i];
buf[i - begin] = (c == oldChar) ? newChar : c;
i++;
}
return new FastStr(buf, 0, len);
}
}
return this;
}
/**
* Wrapper of {@link String#matches(String)}
*
* @param regex the regular expression to checked against this FastStr
* @return {@code true} if this FastStr matches {@code regex}
*/
@Override
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
/**
* Wrapper of {@link String#contains(CharSequence)}
*
* @param s the char sequence to be found
* @return {@code true} if {@code s} has been found
*/
@Override
public boolean contains(CharSequence s) {
if (s instanceof FastStr) {
return indexOf((FastStr) s) > -1;
}
return indexOf(s.toString()) > -1;
}
/**
* Wrapper of {@link String#replaceFirst(String, String)} but return FastStr inance
*
* @param regex the regular expression specifies the place to be replaced
* @param replacement the string to replace the found part
* @return a FastStr that has the first found part replaced
*/
@Override
public FastStr replaceFirst(String regex, String replacement) {
return unsafeOf(Pattern.compile(regex).matcher(this).replaceFirst(replacement));
}
/**
* Wrapper of {@link String#replaceAll(String, String)} but return FastStr type instance
*
* @param regex the regular expression specifies the pattern to be replaced
* @param replacement the replacement string
* @return a FastStr instance with all found part replaced
*/
@Override
public FastStr replaceAll(String regex, String replacement) {
return unsafeOf(Pattern.compile(regex).matcher(this).replaceAll(replacement));
}
/**
* Wrapper of {@link String#replace(CharSequence, CharSequence)} but return FastStr type instance
*
* @param target the char sequence to be replaced
* @param replacement the char sequence used to replace {@code target}
* @return a FastStr instance with all {@code target} being replaced with
* {@code replacement}
*/
@Override
public FastStr replace(CharSequence target, CharSequence replacement) {
return unsafeOf(Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString())));
}
/**
* Wrapper of {@link String#split(String, int)} but return an immutable List of FastStr instances
*
* @param regex the regular expression matches the seperator
* @param limit the result threshold
* @return a {@link org.osgl.util.C.List} of FastStr instances split from this FastStr
*/
@Override
public C.List split(String regex, int limit) {
/* fastpath if the regex is a
(1)one-char String and this character is not one of the
RegEx's meta characters ".$|()[{^?*+\\", or
(2)two-char String and the first char is the backslash and
the second is not the ascii digit or ascii letter.
*/
char ch;
if (((regex.length() == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE)) {
int off = 0;
int next = 0;
boolean limited = limit > 0;
C.List list = C.newList();
while ((next = indexOf(ch, off)) != -1) {
if (!limited || list.size() < limit - 1) {
list.add(substr(off, next));
off = next + 1;
} else { // last one
//assert (list.size() == limit - 1);
list.add(substr(off, buf.length));
off = buf.length;
break;
}
}
// If no match was found, return this
if (off == 0) {
return C.listOf(this);
}
// Add remaining segment
if (!limited || list.size() < limit) {
list.add(substr(off, buf.length));
}
// Construct result
int resultSize = list.size();
if (limit == 0) {
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
}
return list.subList(0, resultSize);
}
String[] sa = Pattern.compile(regex).split(this, limit);
int len = sa.length;
FastStr[] ssa = new FastStr[len];
for (int i = 0; i < len; ++i) {
ssa[i] = unsafeOf(sa[i]);
}
return C.listOf(ssa);
}
/**
* Wrapper of {@link String#toLowerCase(java.util.Locale)} but return FastStr type instance
*
* @param locale the locale
* @return a FastStr instance with all characters from this FastStr
* be converted into lowercase based on the locale specified
*/
public FastStr toLowerCase(Locale locale) {
String s = toString();
return unsafeOf(s.toLowerCase(locale));
}
/**
* Wrapper of {@link String#toUpperCase(java.util.Locale)} but return FastStr type instance
*
* @param locale the locale
* @return a FastStr instance with all characters from this FastStr
* be converted into uppercase based on the locale specified
*/
@Override
public FastStr toUpperCase(Locale locale) {
String s = toString();
return unsafeOf(s.toUpperCase(locale));
}
/**
* Wrapper of {@link String#trim()} and return FastStr type instance
*
* @return a FastStr instance without leading and tail space characters
* from this FastStr instance
*/
@Override
public FastStr trim() {
char[] value = this.buf;
int len = end;
int st = begin;
char[] val = value; /* avoid getfield opcode */
while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
return ((st > begin) || (len < size())) ? new FastStr(value, st, len) : this;
}
/**
* Alias of {@link #toCharArray()}
*
* @return char array buf copy of this FastStr
*/
public char[] charArray() {
char[] newBuf = new char[size()];
copyTo(newBuf, 0);
return newBuf;
}
/**
* Return char array buf of this FastStr instance.
* Note this method might return the char array buf directly
* without copy operation
* @return the char array buf of this FastStr
*/
public char[] unsafeChars() {
if (begin == 0) return buf;
char[] newBuf = new char[size()];
copyTo(newBuf, 0);
return newBuf;
}
/**
* Wrapper of {@link String#intern()}
*
* @return the intern of the string
*/
@Override
public String intern() {
return toString().intern();
}
// -- extensions
@Override
public FastStr afterFirst(String s) {
return afterFirst(unsafeOf(s));
}
@Override
public FastStr afterLast(String s) {
return afterLast(unsafeOf(s));
}
@Override
public FastStr afterFirst(FastStr s) {
int pos = indexOf(s);
if (pos < 0) return EMPTY_STR;
return substr(pos + s.size());
}
@Override
public FastStr afterLast(FastStr s) {
int pos = lastIndexOf(s);
if (pos < 0) return EMPTY_STR;
return substr(pos + s.size());
}
@Override
public FastStr afterFirst(char c) {
int pos = indexOf(c);
if (pos < 0) return EMPTY_STR;
return substr(pos + 1);
}
@Override
public FastStr afterLast(char c) {
int pos = lastIndexOf(c);
if (pos < 0) return EMPTY_STR;
return substr(pos + 1);
}
public FastStr beforeFirst(String s) {
return beforeFirst(unsafeOf(s));
}
public FastStr beforeLast(String s) {
return beforeLast(unsafeOf(s));
}
public FastStr beforeFirst(FastStr s) {
int pos = indexOf(s);
if (pos < 0) return EMPTY_STR;
return substr(0, pos);
}
public FastStr beforeLast(FastStr s) {
int pos = lastIndexOf(s);
if (pos < 0) return EMPTY_STR;
return substr(0, pos);
}
@Override
public FastStr beforeFirst(char c) {
int pos = indexOf(c);
if (pos < 0) return EMPTY_STR;
return substr(0, pos);
}
@Override
public FastStr beforeLast(char c) {
int pos = lastIndexOf(c);
if (pos < 0) return EMPTY_STR;
return substr(0, pos);
}
public FastStr strip(String prefix, String suffix) {
FastStr s = this;
if (startsWith(prefix)) s = s.substr(prefix.length());
if (s.endsWith(suffix)) s = s.substr(0, s.size() - suffix.length());
return s;
}
public FastStr strip(FastStr prefix, FastStr suffix) {
FastStr s = this;
if (startsWith(prefix)) s = s.substr(prefix.size());
if (s.endsWith(suffix)) s = s.substr(0, s.size() - suffix.size());
return s;
}
public FastStr urlEncode() {
return unsafeOf(S.urlEncode(new String(buf)));
}
public FastStr decodeBASE64() {
return unsafeOf(S.decodeBASE64(new String(buf)));
}
public FastStr encodeBASE64() {
return unsafeOf(S.encodeBASE64(new String(buf)));
}
@Override
public FastStr capFirst() {
if (isEmpty()) return this;
int sz = size();
char[] buf = this.buf;
char[] newBuf = S.unsafeCapFirst(buf, begin, sz);
if (buf == newBuf) return this;
return unsafeOf(newBuf, 0, sz);
}
@Override
public int count(String search, boolean overlap) {
char[] searchBuf = search.toCharArray();
return count(searchBuf, 0, searchBuf.length, overlap);
}
@Override
public int count(FastStr search, boolean overlap) {
return count(search.buf, search.begin, search.size(), overlap);
}
private int toInternalId(int index) {
return begin + index;
}
private int toExternalId(int index) {
return index - begin;
}
private void copyTo(char[] buf, int begin) {
System.arraycopy(this.buf, this.begin, buf, begin, size());
}
private int indexOfSupplementary(int ch, int fromIndex) {
if (Character.isValidCodePoint(ch)) {
final char[] buf = this.buf;
final char hi = highSurrogate(ch);
final char lo = lowSurrogate(ch);
final int max = size() - 1;
for (int i = fromIndex; i < max; i++) {
if (buf[toInternalId(i)] == hi && buf[toInternalId(i + 1)] == lo) {
return i;
}
}
}
return -1;
}
private int lastIndexOfSupplementary(int ch, int fromIndex) {
if (Character.isValidCodePoint(ch)) {
final char[] buf = this.buf;
char hi = highSurrogate(ch);
char lo = lowSurrogate(ch);
int i = Math.min(fromIndex, buf.length - 2);
for (; i >= 0; i--) {
if (buf[toInternalId(i)] == hi && buf[toInternalId(i + 1)] == lo) {
return i;
}
}
}
return -1;
}
private int count(char[] search, int searchOffset, int searchCount, boolean overlap) {
if (isEmpty()) return 0;
return S.count(buf, begin, size(), search, searchOffset, searchCount, overlap);
}
// ---- factory methods
/**
* Construct a FastStr from a char array
* @param ca the char array
* @return a FastStr
*/
public static FastStr of(char[] ca) {
if (ca.length == 0) return EMPTY_STR;
char[] newArray = new char[ca.length];
System.arraycopy(ca, 0, newArray, 0, ca.length);
return new FastStr(ca);
}
/**
* Construct a FastStr from a CharSequence
* @param cs the CharSequence instance
* @return a FastStr
*/
public static FastStr of(CharSequence cs) {
if (cs instanceof FastStr) {
return (FastStr)cs;
}
return of(cs.toString());
}
/**
* Construct a FastStr from a String
* @param s the String
* @return a FastStr
*/
public static FastStr of(String s) {
int sz = s.length();
if (sz == 0) return EMPTY_STR;
char[] buf = s.toCharArray();
return new FastStr(buf, 0, sz);
}
/**
* Construct a FastStr from a StringBuilder
* @param sb the string builder
* @return a FastStr
*/
public static FastStr of(StringBuilder sb) {
int sz = sb.length();
if (0 == sz) return EMPTY_STR;
char[] buf = new char[sz];
for (int i = 0; i < sz; ++i) {
buf[i] = sb.charAt(i);
}
return new FastStr(buf, 0, sz);
}
/**
* Construct a FastStr from a StringBuffer
* @param sb the string buffer
* @return the FastStr
*/
public static FastStr of(StringBuffer sb) {
int sz = sb.length();
if (0 == sz) return EMPTY_STR;
char[] buf = new char[sz];
for (int i = 0; i < sz; ++i) {
buf[i] = sb.charAt(i);
}
return new FastStr(buf, 0, sz);
}
/**
* Construct a FastStr instance from an iterable of characters
* @param itr the character iterable
* @return the FastStr
*/
public static FastStr of(Iterable itr) {
StringBuilder sb = new StringBuilder();
for (Character c : itr) {
sb.append(c);
}
return of(sb);
}
/**
* Construct a FastStr instance from a collection of characters
* @param col the character collection
* @return a FastStr instance
*/
public static FastStr of(Collection col) {
int sz = col.size();
if (0 == sz) return EMPTY_STR;
char[] buf = new char[sz];
Iterator itr = col.iterator();
int i = 0;
while (itr.hasNext()) {
buf[i++] = itr.next();
}
return new FastStr(buf, 0, sz);
}
/**
* Construct a FastStr instance from an iterator of characters
* @param itr the character iterator
* @return a FastStr instance consists of all chars in the iterator
*/
public static FastStr of(Iterator itr) {
StringBuilder sb = new StringBuilder();
while (itr.hasNext()) {
sb.append(itr.next());
}
return of(sb);
}
public static FastStr of(byte[] bytes, String encoding) {
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
CharBuffer charBuffer = Charset.forName(encoding).decode(byteBuffer);
char[] chars = Arrays.copyOfRange(charBuffer.array(), charBuffer.position(), charBuffer.limit());
Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data
Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
return FastStr.of(chars);
}
/**
* Construct a FastStr instance from a String instance.
* The FastStr instance will share the char array buf with
* the String instance
* @param s the string instance
* @return A FastStr instance
*/
public static FastStr unsafeOf(String s) {
int sz = s.length();
if (sz == 0) return EMPTY_STR;
char[] buf = s.toCharArray();
return new FastStr(buf, 0, sz);
}
/**
* Construct a FastStr instance from char array without array copying
* @param buf the char array
* @return a FastStr instance from the char array
*/
@SuppressWarnings("unused")
public static FastStr unsafeOf(char[] buf) {
E.NPE(buf);
return new FastStr(buf, 0, buf.length);
}
/**
* Construct a FastStr instance from char array, from the start position, finished at end position
* without copying the array. This method might use the array directly instead of copying elements
* from the array. Thus it is extremely important that the array buf passed in will NOT be updated
* outside the FastStr instance.
* @param buf the char array
* @param start the start position (inclusive)
* @param end the end position (exclusive)
* @return a FastStr instance that consist of chars specified
*/
public static FastStr unsafeOf(char[] buf, int start, int end) {
E.NPE(buf);
E.illegalArgumentIf(start < 0 || end > buf.length);
if (end < start) return EMPTY_STR;
return new FastStr(buf, start, end);
}
/**
* Return the char array that backed the char sequence specified
* @param chars the char sequence
* @return an array of chars of the char sequence
*/
@SuppressWarnings("unused")
@Deprecated
public static char[] bufOf(CharSequence chars) {
return FastStr.of(chars).charArray();
}
private static char highSurrogate(int codePoint) {
return (char) ((codePoint >>> 10)
+ (MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT >>> 10)));
}
private static char lowSurrogate(int codePoint) {
return (char) ((codePoint & 0x3ff) + MIN_LOW_SURROGATE);
}
}