package java.lang;
/**
* Just some utility methods for the wrapper classes as well as StringBuffer and StringBuilder.
* @author Sven Köhler
*/
class StringUtils
{
private static final int STR_NAN_LEN = 3;
private static final String STR_NAN = "NaN";
private static final int STR_INFINITY_LEN = 8;
private static final String STR_INFINITY = "Infinity";
static final int MAX_FLOAT_CHARS = 15;
static final int MAX_DOUBLE_CHARS = 25;
static int parseDigit(char c, int radix)
{
int r = Character.digit((int)c, radix);
if (r < 0)
throw new NumberFormatException("illegal digit character");
return r;
}
/**
* For the parseInt/parseLong methods.
*/
static void throwNumberFormat(String s, int radix)
{
if (s == null)
throw new NumberFormatException("string is null");
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
throw new NumberFormatException("given radix is invalid");
}
/**
* For the toString() methods of Integer/Long.
*/
static int invalidRadixTo10(int radix)
{
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
return 10;
return radix;
}
/**
* Low-level convert of int to char[].
*
* @param p position of the character after the last digit
*/
static int getIntChars(char[] buf, int p, int v, int radix)
{
int v2 = (v <= 0) ? v : -v;
do
{
buf[--p] = Character.forDigit(-(v2 % radix), radix);
v2 /= radix;
} while (v2 != 0);
if (v < 0)
buf[--p] = '-';
return p;
}
/**
* Low-level convert of long to char[].
*
* @param p position of the character after the last digit
*/
static int getLongChars(char[] buf, int p, long v, int radix)
{
long v2 = (v <= 0) ? v : -v;
do
{
buf[--p] = Character.forDigit(-(int)(v2 % radix), radix);
v2 /= radix;
} while (v2 != 0);
if (v < 0)
buf[--p] = '-';
return p;
}
static void reverseChars(char[] buf, int start, int end, int len)
{
len = Math.min(len, (end - start) >> 1);
int end2 = start + len;
int base = start + end - 1;
for (int i = start; i < end2; i++)
{
int j = base - i;
char tmp = buf[i];
buf[i] = buf[j];
buf[j] = tmp;
}
}
/**
* Exact size of buffer for {@link #getIntChars(char[], int, int, int)}.
*/
static int exactStringLength(int v, int radix)
{
int c = (v < 0) ? 1 : 0;
do
{
c++;
v /= radix;
} while (v != 0);
return c;
}
/**
* Exact size of buffer for {@link #getLongChars(char[], int, long, int)}.
*/
static int exactStringLength(long v, int radix)
{
int c = (v < 0) ? 1 : 0;
do
{
c++;
v /= radix;
} while (v != 0);
return c;
}
private static class Pow10FConstants
{
public static final float[] POW10F_1 = {1E+1f, 1E+2f, 1E+4f, 1E+8f, 1E+16f, 1E+32f, };
public static final float[] POW10F_2 = {1E-1f, 1E-2f, 1E-4f, 1E-8f, 1E-16f, 1E-32f, };
}
private static float pow10f_bf(float r, int e)
{
// bf stand for "Big exponent First"
float[] b;
if (e >= 0)
b = Pow10FConstants.POW10F_1;
else
{
b = Pow10FConstants.POW10F_2;
e = -e;
}
if ((e & 0x020) != 0) r *= b[5];
if ((e & 0x010) != 0) r *= b[4];
if ((e & 0x008) != 0) r *= b[3];
if ((e & 0x004) != 0) r *= b[2];
if ((e & 0x002) != 0) r *= b[1];
if ((e & 0x001) != 0) r *= b[0];
return r;
}
private static float pow10f_sf(float r, int e)
{
// sf stand for "Small exponent First"
float[] b;
if (e >= 0)
b = Pow10FConstants.POW10F_1;
else
{
b = Pow10FConstants.POW10F_2;
e = -e;
}
if ((e & 0x001) != 0) r *= b[0];
if ((e & 0x002) != 0) r *= b[1];
if ((e & 0x004) != 0) r *= b[2];
if ((e & 0x008) != 0) r *= b[3];
if ((e & 0x010) != 0) r *= b[4];
if ((e & 0x020) != 0) r *= b[5];
return r;
}
private static class Pow10DConstants
{
public static final double[] POW10D_1 = {1E+1, 1E+2, 1E+4, 1E+8, 1E+16, 1E+32, 1E+64, 1E+128, 1E+256, };
public static final double[] POW10D_2 = {1E-1, 1E-2, 1E-4, 1E-8, 1E-16, 1E-32, 1E-64, 1E-128, 1E-256, };
}
private static double pow10d_bf(double r, int e)
{
// bf stand for "Big exponent First"
double[] b;
if (e >= 0)
b = Pow10DConstants.POW10D_1;
else
{
b = Pow10DConstants.POW10D_2;
e = -e;
}
if ((e & 0x100) != 0) r *= b[8];
if ((e & 0x080) != 0) r *= b[7];
if ((e & 0x040) != 0) r *= b[6];
if ((e & 0x020) != 0) r *= b[5];
if ((e & 0x010) != 0) r *= b[4];
if ((e & 0x008) != 0) r *= b[3];
if ((e & 0x004) != 0) r *= b[2];
if ((e & 0x002) != 0) r *= b[1];
if ((e & 0x001) != 0) r *= b[0];
return r;
}
private static double pow10d_sf(double r, int e)
{
// sf stand for "Small exponent First"
double[] b;
if (e >= 0)
b = Pow10DConstants.POW10D_1;
else
{
b = Pow10DConstants.POW10D_2;
e = -e;
}
if ((e & 0x001) != 0) r *= b[0];
if ((e & 0x002) != 0) r *= b[1];
if ((e & 0x004) != 0) r *= b[2];
if ((e & 0x008) != 0) r *= b[3];
if ((e & 0x010) != 0) r *= b[4];
if ((e & 0x020) != 0) r *= b[5];
if ((e & 0x040) != 0) r *= b[6];
if ((e & 0x080) != 0) r *= b[7];
if ((e & 0x100) != 0) r *= b[8];
return r;
}
static int getDoubleChars(double x, char[] sb, int p)
{
if (x != x)
{
STR_NAN.getChars(0, STR_NAN_LEN, sb, p);
return p + STR_NAN_LEN;
}
//we need to detect -0.0 to be compatible with JDK
long bits = Double.doubleToRawLongBits(x);
if ((bits & 0x8000000000000000L) != 0)
{
sb[p++] = '-';
x = -x;
}
if (x == 0)
{
sb[p] = '0';
sb[p+1] = '.';
sb[p+2] = '0';
return p+3;
}
if (x == Double.POSITIVE_INFINITY)
{
STR_INFINITY.getChars(0, STR_INFINITY_LEN, sb, p);
return p + STR_INFINITY_LEN;
}
int exp;
if (x >= Double.MIN_NORMAL)
exp = 62;
else
{
exp = 0;
bits = Double.doubleToRawLongBits(x * 0x1p62);
}
exp += (int)(bits >> 52) & 0x7FF;
exp = ((exp * 631305) >> 21) - 327;
// at this point, the following should always hold:
// floor(log(10, x)) - 1 < exp <= floor(log(10, x))
x = pow10d_bf(x, 14 - exp);
long tmp = 1000000000000000L;
long digits = (long)(x + 0.5);
if (digits >= tmp)
{
exp++;
digits = (long)(x * 0.1 + 0.5);
}
// algorithm shows true value of subnormal doubles
// unfortunatly, the mantisse of subnormal values gets very short
// TODO automatically adjust digit count for subnormal values
int leading = 0;
if (exp > 0 && exp < 7)
{
leading = exp;
exp = 0;
}
for (int i=0; i<=leading; i++)
{
int d = (int)(digits / (tmp /= 10));
sb[p++] = (char)('0' + d);
digits -= tmp * d;
}
sb[p++] = '.';
do
{
int d = (int)(digits / (tmp /= 10));
sb[p++] = (char)('0' + d);
digits -= tmp * d;
}
while (digits > 0);
if (exp != 0)
{
sb[p++] = 'E';
if (exp < 0)
{
sb[p++] = '-';
exp = -exp;
}
if (exp >= 100)
sb[p++] = (char)(exp / 100 + '0');
if (exp >= 10)
sb[p++] = (char)(exp / 10 % 10 + '0');
sb[p++] = (char)(exp % 10 + '0');
}
return p;
}
static int getFloatChars(float x, char[] sb, int p)
{
if (x != x)
{
STR_NAN.getChars(0, STR_NAN_LEN, sb, p);
return p + STR_NAN_LEN;
}
//we need to detect -0.0 to be compatible with JDK
int bits = Float.floatToRawIntBits(x);
if ((bits & 0x80000000) != 0)
{
sb[p++] = '-';
x = -x;
}
if (x == 0)
{
sb[p] = '0';
sb[p+1] = '.';
sb[p+2] = '0';
return p+3;
}
if (x == Float.POSITIVE_INFINITY)
{
STR_INFINITY.getChars(0, STR_INFINITY_LEN, sb, p);
return p + STR_INFINITY_LEN;
}
int exp;
if (x >= Float.MIN_NORMAL)
exp = 31;
else
{
exp = 0;
bits = Float.floatToRawIntBits(x * 0x1p31f);
}
exp += (bits >> 23) & 0xFF;
exp = ((exp * 5050455) >> 24) - 48;
// at this point, the following should always hold:
// floor(log(10, x)) - 1 < exp <= floor(log(10, x))
x = pow10f_bf(x, 6 - exp);
int tmp = 10000000;
int digits = (int)(x + 0.5f);
if (digits >= tmp)
{
exp++;
digits = (int)(x * 0.1f + 0.5f);
}
// algorithm shows true value of subnormal doubles
// unfortunatly, the mantisse of subnormal values gets very short
// TODO automatically adjust digit count for subnormal values
int leading = 0;
if (exp > 0 && exp < 6)
{
leading = exp;
exp = 0;
}
for (int i=0; i<=leading; i++)
{
int d = digits / (tmp /= 10);
sb[p++] = (char)('0' + d);
digits -= tmp * d;
}
sb[p++] = '.';
do
{
int d = digits / (tmp /= 10);
sb[p++] = (char)('0' + d);
digits -= tmp * d;
}
while (digits > 0);
if (exp != 0)
{
sb[p++] = 'E';
if (exp < 0)
{
sb[p++] = '-';
exp = -exp;
}
if (exp >= 10)
sb[p++] = (char)(exp / 10 + '0');
sb[p++] = (char)(exp % 10 + '0');
}
return p;
}
private static boolean checkString(String s, int p, String c)
{
int l = c.length();
for (int i=0; i<l; i++)
if (s.charAt(p+i) != c.charAt(i))
return false;
return true;
}
/**
* Roughly equals abs(minimal exponent of subnormal double in base 10) + digits of long
*/
private static final int STR_TO_DOUBLE_MAXEXP = 350;
static double stringToDouble(String s)
{
long r = 0;
int exp = 0;
int l = s.length();
if (l <= 0)
throw new NumberFormatException();
int p;
boolean neg;
switch (s.charAt(0))
{
case '-':
p = 1;
neg = true;
break;
case '+':
p = 1;
neg = false;
break;
default:
p = 0;
neg = false;
}
switch (l-p)
{
case STR_NAN_LEN:
if (checkString(s, p, STR_NAN))
return Double.NaN;
break;
case STR_INFINITY_LEN:
if (checkString(s, p, STR_INFINITY))
return neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
break;
}
boolean digits = false;
while (p < l)
{
char c = s.charAt(p);
if (c < '0' || c > '9')
{
if (c == '.' || c == 'e' || c == 'E')
break;
throw new NumberFormatException();
}
digits = true;
if (r <= (Long.MAX_VALUE - 9) / 10)
r = r * 10 + (c - '0');
else
exp++;
p++;
}
if (p < l && s.charAt(p) == '.')
{
p++;
while (p < l)
{
char c = s.charAt(p);
if (c < '0' || c > '9')
{
if (c == 'e' || c == 'E')
break;
throw new NumberFormatException();
}
digits = true;
if (r <= (Long.MAX_VALUE - 9) / 10)
{
r = r * 10 + (c - '0');
exp--;
}
p++;
}
}
if (!digits)
throw new NumberFormatException();
if (p < l)
{
//at this point, s.charAt(p) has to be 'e' or 'E'
p++;
boolean digitsexp = false;
boolean negexp;
if (p < l)
{
switch (s.charAt(p))
{
case '-':
negexp = true;
exp = -exp;
p++;
break;
case '+':
p++;
default:
negexp = false;
}
}
else
negexp = false;
int exp2 = 0;
while (p < l)
{
char c = s.charAt(p);
if (c < '0' || c > '9')
throw new NumberFormatException();
digitsexp = true;
if (exp2 + exp < STR_TO_DOUBLE_MAXEXP)
exp2 = exp2 * 10 + (c - '0');
p++;
}
if (!digitsexp)
throw new NumberFormatException();
exp2 += exp;
exp = negexp ? -exp2 : exp2;
}
double r2;
if (exp < -STR_TO_DOUBLE_MAXEXP)
r2 = 0.0;
else if (exp > STR_TO_DOUBLE_MAXEXP)
r2 = Double.POSITIVE_INFINITY;
else
r2 = pow10d_sf(r, exp);
return neg ? -r2 : r2;
}
/**
* Roughly equals abs(minimal exponent of subnormal float in base 10) + digits of int
*/
private static final int STR_TO_FLOAT_MAXEXP = 60;
static float stringToFloat(String s)
{
int r = 0;
int exp = 0;
int l = s.length();
if (l <= 0)
throw new NumberFormatException();
int p;
boolean neg;
switch (s.charAt(0))
{
case '-':
p = 1;
neg = true;
break;
case '+':
p = 1;
neg = false;
break;
default:
p = 0;
neg = false;
}
switch (l-p)
{
case STR_NAN_LEN:
if (checkString(s, p, STR_NAN))
return Float.NaN;
break;
case STR_INFINITY_LEN:
if (checkString(s, p, STR_INFINITY))
return neg ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
break;
}
boolean digits = false;
while (p < l)
{
char c = s.charAt(p);
if (c < '0' || c > '9')
{
if (c == '.' || c == 'e' || c == 'E')
break;
throw new NumberFormatException();
}
digits = true;
if (r <= (Integer.MAX_VALUE - 9) / 10)
r = r * 10 + (c - '0');
else
exp++;
p++;
}
if (p < l && s.charAt(p) == '.')
{
p++;
while (p < l)
{
char c = s.charAt(p);
if (c < '0' || c > '9')
{
if (c == 'e' || c == 'E')
break;
throw new NumberFormatException();
}
digits = true;
if (r <= (Integer.MAX_VALUE - 9) / 10)
{
r = r * 10 + (c - '0');
exp--;
}
p++;
}
}
if (!digits)
throw new NumberFormatException();
if (p < l)
{
//at this point, s.charAt(p) has to be 'e' or 'E'
p++;
boolean digitsexp = false;
boolean negexp;
if (p < l)
{
switch (s.charAt(p))
{
case '-':
negexp = true;
exp = -exp;
p++;
break;
case '+':
p++;
default:
negexp = false;
}
}
else
negexp = false;
int exp2 = 0;
while (p < l)
{
char c = s.charAt(p);
if (c < '0' || c > '9')
throw new NumberFormatException();
digitsexp = true;
if (exp2 + exp < STR_TO_FLOAT_MAXEXP)
exp2 = exp2 * 10 + (c - '0');
p++;
}
if (!digitsexp)
throw new NumberFormatException();
exp2 += exp;
exp = negexp ? -exp2 : exp2;
}
float r2;
if (exp < -STR_TO_FLOAT_MAXEXP)
r2 = 0.0f;
else if (exp > STR_TO_FLOAT_MAXEXP)
r2 = Float.POSITIVE_INFINITY;
else
r2 = pow10f_sf(r, exp);
return neg ? -r2 : r2;
}
}