blob: 9ab9d49b85952732116ba327343f5aed127f1088 [file] [log] [blame]
package org.codehaus.jackson.io;
public final class NumberOutput
{
private final static char NULL_CHAR = (char) 0;
private static int MILLION = 1000000;
private static int BILLION = 1000000000;
private static long TEN_BILLION_L = 10000000000L;
private static long THOUSAND_L = 1000L;
private static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE;
private static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE;
final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE);
final static char[] LEADING_TRIPLETS = new char[4000];
final static char[] FULL_TRIPLETS = new char[4000];
static {
/* Let's fill it with NULLs for ignorable leading digits,
* and digit chars for others
*/
int ix = 0;
for (int i1 = 0; i1 < 10; ++i1) {
char f1 = (char) ('0' + i1);
char l1 = (i1 == 0) ? NULL_CHAR : f1;
for (int i2 = 0; i2 < 10; ++i2) {
char f2 = (char) ('0' + i2);
char l2 = (i1 == 0 && i2 == 0) ? NULL_CHAR : f2;
for (int i3 = 0; i3 < 10; ++i3) {
// Last is never to be empty
char f3 = (char) ('0' + i3);
LEADING_TRIPLETS[ix] = l1;
LEADING_TRIPLETS[ix+1] = l2;
LEADING_TRIPLETS[ix+2] = f3;
FULL_TRIPLETS[ix] = f1;
FULL_TRIPLETS[ix+1] = f2;
FULL_TRIPLETS[ix+2] = f3;
ix += 4;
}
}
}
}
final static byte[] FULL_TRIPLETS_B = new byte[4000];
static {
for (int i = 0; i < 4000; ++i) {
FULL_TRIPLETS_B[i] = (byte) FULL_TRIPLETS[i];
}
}
final static String[] sSmallIntStrs = new String[] {
"0","1","2","3","4","5","6","7","8","9","10"
};
final static String[] sSmallIntStrs2 = new String[] {
"-1","-2","-3","-4","-5","-6","-7","-8","-9","-10"
};
/*
/**********************************************************
/* Efficient serialization methods using raw buffers
/**********************************************************
*/
/**
* @return Offset within buffer after outputting int
*/
public static int outputInt(int value, char[] buffer, int offset)
{
if (value < 0) {
if (value == Integer.MIN_VALUE) {
/* Special case: no matching positive value within range;
* let's then "upgrade" to long and output as such.
*/
return outputLong((long) value, buffer, offset);
}
buffer[offset++] = '-';
value = -value;
}
if (value < MILLION) { // at most 2 triplets...
if (value < 1000) {
if (value < 10) {
buffer[offset++] = (char) ('0' + value);
} else {
offset = outputLeadingTriplet(value, buffer, offset);
}
} else {
int thousands = value / 1000;
value -= (thousands * 1000); // == value % 1000
offset = outputLeadingTriplet(thousands, buffer, offset);
offset = outputFullTriplet(value, buffer, offset);
}
return offset;
}
// ok, all 3 triplets included
/* Let's first hand possible billions separately before
* handling 3 triplets. This is possible since we know we
* can have at most '2' as billion count.
*/
boolean hasBillions = (value >= BILLION);
if (hasBillions) {
value -= BILLION;
if (value >= BILLION) {
value -= BILLION;
buffer[offset++] = '2';
} else {
buffer[offset++] = '1';
}
}
int newValue = value / 1000;
int ones = (value - (newValue * 1000)); // == value % 1000
value = newValue;
newValue /= 1000;
int thousands = (value - (newValue * 1000));
// value now has millions, which have 1, 2 or 3 digits
if (hasBillions) {
offset = outputFullTriplet(newValue, buffer, offset);
} else {
offset = outputLeadingTriplet(newValue, buffer, offset);
}
offset = outputFullTriplet(thousands, buffer, offset);
offset = outputFullTriplet(ones, buffer, offset);
return offset;
}
public static int outputInt(int value, byte[] buffer, int offset)
{
if (value < 0) {
if (value == Integer.MIN_VALUE) {
return outputLong((long) value, buffer, offset);
}
buffer[offset++] = '-';
value = -value;
}
if (value < MILLION) { // at most 2 triplets...
if (value < 1000) {
if (value < 10) {
buffer[offset++] = (byte) ('0' + value);
} else {
offset = outputLeadingTriplet(value, buffer, offset);
}
} else {
int thousands = value / 1000;
value -= (thousands * 1000); // == value % 1000
offset = outputLeadingTriplet(thousands, buffer, offset);
offset = outputFullTriplet(value, buffer, offset);
}
return offset;
}
boolean hasBillions = (value >= BILLION);
if (hasBillions) {
value -= BILLION;
if (value >= BILLION) {
value -= BILLION;
buffer[offset++] = '2';
} else {
buffer[offset++] = '1';
}
}
int newValue = value / 1000;
int ones = (value - (newValue * 1000)); // == value % 1000
value = newValue;
newValue /= 1000;
int thousands = (value - (newValue * 1000));
if (hasBillions) {
offset = outputFullTriplet(newValue, buffer, offset);
} else {
offset = outputLeadingTriplet(newValue, buffer, offset);
}
offset = outputFullTriplet(thousands, buffer, offset);
offset = outputFullTriplet(ones, buffer, offset);
return offset;
}
/**
* @return Offset within buffer after outputting int
*/
public static int outputLong(long value, char[] buffer, int offset)
{
// First: does it actually fit in an int?
if (value < 0L) {
/* MIN_INT is actually printed as long, just because its
* negation is not an int but long
*/
if (value > MIN_INT_AS_LONG) {
return outputInt((int) value, buffer, offset);
}
if (value == Long.MIN_VALUE) {
// Special case: no matching positive value within range
int len = SMALLEST_LONG.length();
SMALLEST_LONG.getChars(0, len, buffer, offset);
return (offset + len);
}
buffer[offset++] = '-';
value = -value;
} else {
if (value <= MAX_INT_AS_LONG) {
return outputInt((int) value, buffer, offset);
}
}
/* Ok: real long print. Need to first figure out length
* in characters, and then print in from end to beginning
*/
int origOffset = offset;
offset += calcLongStrLength(value);
int ptr = offset;
// First, with long arithmetics:
while (value > MAX_INT_AS_LONG) { // full triplet
ptr -= 3;
long newValue = value / THOUSAND_L;
int triplet = (int) (value - newValue * THOUSAND_L);
outputFullTriplet(triplet, buffer, ptr);
value = newValue;
}
// Then with int arithmetics:
int ivalue = (int) value;
while (ivalue >= 1000) { // still full triplet
ptr -= 3;
int newValue = ivalue / 1000;
int triplet = ivalue - (newValue * 1000);
outputFullTriplet(triplet, buffer, ptr);
ivalue = newValue;
}
// And finally, if anything remains, partial triplet
outputLeadingTriplet(ivalue, buffer, origOffset);
return offset;
}
public static int outputLong(long value, byte[] buffer, int offset)
{
if (value < 0L) {
if (value > MIN_INT_AS_LONG) {
return outputInt((int) value, buffer, offset);
}
if (value == Long.MIN_VALUE) {
// Special case: no matching positive value within range
int len = SMALLEST_LONG.length();
for (int i = 0; i < len; ++i) {
buffer[offset++] = (byte) SMALLEST_LONG.charAt(i);
}
return offset;
}
buffer[offset++] = '-';
value = -value;
} else {
if (value <= MAX_INT_AS_LONG) {
return outputInt((int) value, buffer, offset);
}
}
int origOffset = offset;
offset += calcLongStrLength(value);
int ptr = offset;
// First, with long arithmetics:
while (value > MAX_INT_AS_LONG) { // full triplet
ptr -= 3;
long newValue = value / THOUSAND_L;
int triplet = (int) (value - newValue * THOUSAND_L);
outputFullTriplet(triplet, buffer, ptr);
value = newValue;
}
// Then with int arithmetics:
int ivalue = (int) value;
while (ivalue >= 1000) { // still full triplet
ptr -= 3;
int newValue = ivalue / 1000;
int triplet = ivalue - (newValue * 1000);
outputFullTriplet(triplet, buffer, ptr);
ivalue = newValue;
}
outputLeadingTriplet(ivalue, buffer, origOffset);
return offset;
}
/*
/**********************************************************
/* Secondary convenience serialization methods
/**********************************************************
*/
/* !!! 05-Aug-2008, tatus: Any ways to further optimize
* these? (or need: only called by diagnostics methods?)
*/
public static String toString(int value)
{
// Lookup table for small values
if (value < sSmallIntStrs.length) {
if (value >= 0) {
return sSmallIntStrs[value];
}
int v2 = -value - 1;
if (v2 < sSmallIntStrs2.length) {
return sSmallIntStrs2[v2];
}
}
return Integer.toString(value);
}
public static String toString(long value)
{
if (value <= Integer.MAX_VALUE &&
value >= Integer.MIN_VALUE) {
return toString((int) value);
}
return Long.toString(value);
}
public static String toString(double value)
{
return Double.toString(value);
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
private static int outputLeadingTriplet(int triplet, char[] buffer, int offset)
{
int digitOffset = (triplet << 2);
char c = LEADING_TRIPLETS[digitOffset++];
if (c != NULL_CHAR) {
buffer[offset++] = c;
}
c = LEADING_TRIPLETS[digitOffset++];
if (c != NULL_CHAR) {
buffer[offset++] = c;
}
// Last is required to be non-empty
buffer[offset++] = LEADING_TRIPLETS[digitOffset];
return offset;
}
private static int outputLeadingTriplet(int triplet, byte[] buffer, int offset)
{
int digitOffset = (triplet << 2);
char c = LEADING_TRIPLETS[digitOffset++];
if (c != NULL_CHAR) {
buffer[offset++] = (byte) c;
}
c = LEADING_TRIPLETS[digitOffset++];
if (c != NULL_CHAR) {
buffer[offset++] = (byte) c;
}
// Last is required to be non-empty
buffer[offset++] = (byte) LEADING_TRIPLETS[digitOffset];
return offset;
}
private static int outputFullTriplet(int triplet, char[] buffer, int offset)
{
int digitOffset = (triplet << 2);
buffer[offset++] = FULL_TRIPLETS[digitOffset++];
buffer[offset++] = FULL_TRIPLETS[digitOffset++];
buffer[offset++] = FULL_TRIPLETS[digitOffset];
return offset;
}
private static int outputFullTriplet(int triplet, byte[] buffer, int offset)
{
int digitOffset = (triplet << 2);
buffer[offset++] = FULL_TRIPLETS_B[digitOffset++];
buffer[offset++] = FULL_TRIPLETS_B[digitOffset++];
buffer[offset++] = FULL_TRIPLETS_B[digitOffset];
return offset;
}
/**
*<p>
* Pre-conditions: posValue is positive, and larger than
* Integer.MAX_VALUE (about 2 billions).
*/
private static int calcLongStrLength(long posValue)
{
int len = 10;
long comp = TEN_BILLION_L;
// 19 is longest, need to worry about overflow
while (posValue >= comp) {
if (len == 19) {
break;
}
++len;
comp = (comp << 3) + (comp << 1); // 10x
}
return len;
}
}