package com.fasterxml.jackson.core.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; } }