| 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; |
| } |
| } |