| package com.fasterxml.jackson.core.io; |
| |
| public final class NumberOutput |
| { |
| private static int MILLION = 1000000; |
| private static int BILLION = 1000000000; |
| private static long BILLION_L = 1000000000L; |
| |
| 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_INT = String.valueOf(Integer.MIN_VALUE); |
| final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE); |
| |
| /** |
| * Encoded representations of 3-decimal-digit indexed values, where |
| * 3 LSB are ascii characters |
| * |
| * @since 2.8.2 |
| */ |
| private final static int[] TRIPLET_TO_CHARS = new int[1000]; |
| |
| static { |
| /* Let's fill it with NULLs for ignorable leading digits, |
| * and digit chars for others |
| */ |
| int fullIx = 0; |
| for (int i1 = 0; i1 < 10; ++i1) { |
| for (int i2 = 0; i2 < 10; ++i2) { |
| for (int i3 = 0; i3 < 10; ++i3) { |
| int enc = ((i1 + '0') << 16) |
| | ((i2 + '0') << 8) |
| | (i3 + '0'); |
| TRIPLET_TO_CHARS[fullIx++] = enc; |
| } |
| } |
| } |
| } |
| |
| private final static String[] sSmallIntStrs = new String[] { |
| "0","1","2","3","4","5","6","7","8","9","10" |
| }; |
| private 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 v, char[] b, int off) |
| { |
| if (v < 0) { |
| if (v == Integer.MIN_VALUE) { |
| // Special case: no matching positive value within range; |
| // let's then "upgrade" to long and output as such. |
| return _outputSmallestI(b, off); |
| } |
| b[off++] = '-'; |
| v = -v; |
| } |
| |
| if (v < MILLION) { // at most 2 triplets... |
| if (v < 1000) { |
| if (v < 10) { |
| b[off] = (char) ('0' + v); |
| return off+1; |
| } |
| return _leading3(v, b, off); |
| } |
| int thousands = v / 1000; |
| v -= (thousands * 1000); // == value % 1000 |
| off = _leading3(thousands, b, off); |
| off = _full3(v, b, off); |
| return off; |
| } |
| |
| // 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. |
| */ |
| if (v >= BILLION) { |
| v -= BILLION; |
| if (v >= BILLION) { |
| v -= BILLION; |
| b[off++] = '2'; |
| } else { |
| b[off++] = '1'; |
| } |
| return _outputFullBillion(v, b, off); |
| } |
| int newValue = v / 1000; |
| int ones = (v - (newValue * 1000)); // == value % 1000 |
| v = newValue; |
| newValue /= 1000; |
| int thousands = (v - (newValue * 1000)); |
| |
| off = _leading3(newValue, b, off); |
| off = _full3(thousands, b, off); |
| return _full3(ones, b, off); |
| } |
| |
| public static int outputInt(int v, byte[] b, int off) |
| { |
| if (v < 0) { |
| if (v == Integer.MIN_VALUE) { |
| return _outputSmallestI(b, off); |
| } |
| b[off++] = '-'; |
| v = -v; |
| } |
| |
| if (v < MILLION) { // at most 2 triplets... |
| if (v < 1000) { |
| if (v < 10) { |
| b[off++] = (byte) ('0' + v); |
| } else { |
| off = _leading3(v, b, off); |
| } |
| } else { |
| int thousands = v / 1000; |
| v -= (thousands * 1000); // == value % 1000 |
| off = _leading3(thousands, b, off); |
| off = _full3(v, b, off); |
| } |
| return off; |
| } |
| if (v >= BILLION) { |
| v -= BILLION; |
| if (v >= BILLION) { |
| v -= BILLION; |
| b[off++] = '2'; |
| } else { |
| b[off++] = '1'; |
| } |
| return _outputFullBillion(v, b, off); |
| } |
| int newValue = v / 1000; |
| int ones = (v - (newValue * 1000)); // == value % 1000 |
| v = newValue; |
| newValue /= 1000; |
| int thousands = (v - (newValue * 1000)); |
| off = _leading3(newValue, b, off); |
| off = _full3(thousands, b, off); |
| return _full3(ones, b, off); |
| } |
| |
| /** |
| * @return Offset within buffer after outputting int |
| */ |
| public static int outputLong(long v, char[] b, int off) |
| { |
| // First: does it actually fit in an int? |
| if (v < 0L) { |
| if (v > MIN_INT_AS_LONG) { |
| return outputInt((int) v, b, off); |
| } |
| if (v == Long.MIN_VALUE) { |
| return _outputSmallestL(b, off); |
| } |
| b[off++] = '-'; |
| v = -v; |
| } else { |
| if (v <= MAX_INT_AS_LONG) { |
| return outputInt((int) v, b, off); |
| } |
| } |
| |
| // Ok, let's separate last 9 digits (3 x full sets of 3) |
| long upper = v / BILLION_L; |
| v -= (upper * BILLION_L); |
| |
| // two integers? |
| if (upper < BILLION_L) { |
| off = _outputUptoBillion((int) upper, b, off); |
| } else { |
| // no, two ints and bits; hi may be about 16 or so |
| long hi = upper / BILLION_L; |
| upper -= (hi * BILLION_L); |
| off = _leading3((int) hi, b, off); |
| off = _outputFullBillion((int) upper, b, off); |
| } |
| return _outputFullBillion((int) v, b, off); |
| } |
| |
| public static int outputLong(long v, byte[] b, int off) |
| { |
| if (v < 0L) { |
| if (v > MIN_INT_AS_LONG) { |
| return outputInt((int) v, b, off); |
| } |
| if (v == Long.MIN_VALUE) { |
| return _outputSmallestL(b, off); |
| } |
| b[off++] = '-'; |
| v = -v; |
| } else { |
| if (v <= MAX_INT_AS_LONG) { |
| return outputInt((int) v, b, off); |
| } |
| } |
| |
| // Ok, let's separate last 9 digits (3 x full sets of 3) |
| long upper = v / BILLION_L; |
| v -= (upper * BILLION_L); |
| |
| // two integers? |
| if (upper < BILLION_L) { |
| off = _outputUptoBillion((int) upper, b, off); |
| } else { |
| // no, two ints and bits; hi may be about 16 or so |
| long hi = upper / BILLION_L; |
| upper -= (hi * BILLION_L); |
| off = _leading3((int) hi, b, off); |
| off = _outputFullBillion((int) upper, b, off); |
| } |
| return _outputFullBillion((int) v, b, off); |
| } |
| |
| /* |
| /********************************************************** |
| /* 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 v) |
| { |
| // Lookup table for small values |
| if (v < sSmallIntStrs.length) { |
| if (v >= 0) { |
| return sSmallIntStrs[v]; |
| } |
| int v2 = -v - 1; |
| if (v2 < sSmallIntStrs2.length) { |
| return sSmallIntStrs2[v2]; |
| } |
| } |
| return Integer.toString(v); |
| } |
| |
| public static String toString(long v) { |
| if (v <= Integer.MAX_VALUE && v >= Integer.MIN_VALUE) { |
| return toString((int) v); |
| } |
| return Long.toString(v); |
| } |
| |
| public static String toString(double v) { |
| return Double.toString(v); |
| } |
| |
| /** |
| * @since 2.6.0 |
| */ |
| public static String toString(float v) { |
| return Float.toString(v); |
| } |
| |
| /* |
| /********************************************************** |
| /* Internal helper methods |
| /********************************************************** |
| */ |
| |
| private static int _outputUptoBillion(int v, char[] b, int off) |
| { |
| if (v < MILLION) { // at most 2 triplets... |
| if (v < 1000) { |
| return _leading3(v, b, off); |
| } |
| int thousands = v / 1000; |
| int ones = v - (thousands * 1000); // == value % 1000 |
| return _outputUptoMillion(b, off, thousands, ones); |
| } |
| int thousands = v / 1000; |
| int ones = (v - (thousands * 1000)); // == value % 1000 |
| int millions = thousands / 1000; |
| thousands -= (millions * 1000); |
| |
| off = _leading3(millions, b, off); |
| |
| int enc = TRIPLET_TO_CHARS[thousands]; |
| b[off++] = (char) (enc >> 16); |
| b[off++] = (char) ((enc >> 8) & 0x7F); |
| b[off++] = (char) (enc & 0x7F); |
| |
| enc = TRIPLET_TO_CHARS[ones]; |
| b[off++] = (char) (enc >> 16); |
| b[off++] = (char) ((enc >> 8) & 0x7F); |
| b[off++] = (char) (enc & 0x7F); |
| |
| return off; |
| } |
| |
| private static int _outputFullBillion(int v, char[] b, int off) |
| { |
| int thousands = v / 1000; |
| int ones = (v - (thousands * 1000)); // == value % 1000 |
| int millions = thousands / 1000; |
| |
| int enc = TRIPLET_TO_CHARS[millions]; |
| b[off++] = (char) (enc >> 16); |
| b[off++] = (char) ((enc >> 8) & 0x7F); |
| b[off++] = (char) (enc & 0x7F); |
| |
| thousands -= (millions * 1000); |
| enc = TRIPLET_TO_CHARS[thousands]; |
| b[off++] = (char) (enc >> 16); |
| b[off++] = (char) ((enc >> 8) & 0x7F); |
| b[off++] = (char) (enc & 0x7F); |
| |
| enc = TRIPLET_TO_CHARS[ones]; |
| b[off++] = (char) (enc >> 16); |
| b[off++] = (char) ((enc >> 8) & 0x7F); |
| b[off++] = (char) (enc & 0x7F); |
| |
| return off; |
| } |
| |
| private static int _outputUptoBillion(int v, byte[] b, int off) |
| { |
| if (v < MILLION) { // at most 2 triplets... |
| if (v < 1000) { |
| return _leading3(v, b, off); |
| } |
| int thousands = v / 1000; |
| int ones = v - (thousands * 1000); // == value % 1000 |
| return _outputUptoMillion(b, off, thousands, ones); |
| } |
| int thousands = v / 1000; |
| int ones = (v - (thousands * 1000)); // == value % 1000 |
| int millions = thousands / 1000; |
| thousands -= (millions * 1000); |
| |
| off = _leading3(millions, b, off); |
| |
| int enc = TRIPLET_TO_CHARS[thousands]; |
| b[off++] = (byte) (enc >> 16); |
| b[off++] = (byte) (enc >> 8); |
| b[off++] = (byte) enc; |
| |
| enc = TRIPLET_TO_CHARS[ones]; |
| b[off++] = (byte) (enc >> 16); |
| b[off++] = (byte) (enc >> 8); |
| b[off++] = (byte) enc; |
| |
| return off; |
| } |
| |
| private static int _outputFullBillion(int v, byte[] b, int off) |
| { |
| int thousands = v / 1000; |
| int ones = (v - (thousands * 1000)); // == value % 1000 |
| int millions = thousands / 1000; |
| thousands -= (millions * 1000); |
| |
| int enc = TRIPLET_TO_CHARS[millions]; |
| b[off++] = (byte) (enc >> 16); |
| b[off++] = (byte) (enc >> 8); |
| b[off++] = (byte) enc; |
| |
| enc = TRIPLET_TO_CHARS[thousands]; |
| b[off++] = (byte) (enc >> 16); |
| b[off++] = (byte) (enc >> 8); |
| b[off++] = (byte) enc; |
| |
| enc = TRIPLET_TO_CHARS[ones]; |
| b[off++] = (byte) (enc >> 16); |
| b[off++] = (byte) (enc >> 8); |
| b[off++] = (byte) enc; |
| |
| return off; |
| } |
| |
| private static int _outputUptoMillion(char[] b, int off, int thousands, int ones) |
| { |
| int enc = TRIPLET_TO_CHARS[thousands]; |
| if (thousands > 9) { |
| if (thousands > 99) { |
| b[off++] = (char) (enc >> 16); |
| } |
| b[off++] = (char) ((enc >> 8) & 0x7F); |
| } |
| b[off++] = (char) (enc & 0x7F); |
| // and then full |
| enc = TRIPLET_TO_CHARS[ones]; |
| b[off++] = (char) (enc >> 16); |
| b[off++] = (char) ((enc >> 8) & 0x7F); |
| b[off++] = (char) (enc & 0x7F); |
| return off; |
| } |
| |
| private static int _outputUptoMillion(byte[] b, int off, int thousands, int ones) |
| { |
| int enc = TRIPLET_TO_CHARS[thousands]; |
| if (thousands > 9) { |
| if (thousands > 99) { |
| b[off++] = (byte) (enc >> 16); |
| } |
| b[off++] = (byte) (enc >> 8); |
| } |
| b[off++] = (byte) enc; |
| // and then full |
| enc = TRIPLET_TO_CHARS[ones]; |
| b[off++] = (byte) (enc >> 16); |
| b[off++] = (byte) (enc >> 8); |
| b[off++] = (byte) enc; |
| return off; |
| } |
| |
| private static int _leading3(int t, char[] b, int off) |
| { |
| int enc = TRIPLET_TO_CHARS[t]; |
| if (t > 9) { |
| if (t > 99) { |
| b[off++] = (char) (enc >> 16); |
| } |
| b[off++] = (char) ((enc >> 8) & 0x7F); |
| } |
| b[off++] = (char) (enc & 0x7F); |
| return off; |
| } |
| |
| private static int _leading3(int t, byte[] b, int off) |
| { |
| int enc = TRIPLET_TO_CHARS[t]; |
| if (t > 9) { |
| if (t > 99) { |
| b[off++] = (byte) (enc >> 16); |
| } |
| b[off++] = (byte) (enc >> 8); |
| } |
| b[off++] = (byte) enc; |
| return off; |
| } |
| |
| private static int _full3(int t, char[] b, int off) |
| { |
| int enc = TRIPLET_TO_CHARS[t]; |
| b[off++] = (char) (enc >> 16); |
| b[off++] = (char) ((enc >> 8) & 0x7F); |
| b[off++] = (char) (enc & 0x7F); |
| return off; |
| } |
| |
| private static int _full3(int t, byte[] b, int off) |
| { |
| int enc = TRIPLET_TO_CHARS[t]; |
| b[off++] = (byte) (enc >> 16); |
| b[off++] = (byte) (enc >> 8); |
| b[off++] = (byte) enc; |
| return off; |
| } |
| |
| // // // Special cases for where we can not flip the sign bit |
| |
| private static int _outputSmallestL(char[] b, int off) |
| { |
| int len = SMALLEST_LONG.length(); |
| SMALLEST_LONG.getChars(0, len, b, off); |
| return (off + len); |
| } |
| |
| private static int _outputSmallestL(byte[] b, int off) |
| { |
| int len = SMALLEST_LONG.length(); |
| for (int i = 0; i < len; ++i) { |
| b[off++] = (byte) SMALLEST_LONG.charAt(i); |
| } |
| return off; |
| } |
| |
| private static int _outputSmallestI(char[] b, int off) |
| { |
| int len = SMALLEST_INT.length(); |
| SMALLEST_INT.getChars(0, len, b, off); |
| return (off + len); |
| } |
| |
| private static int _outputSmallestI(byte[] b, int off) |
| { |
| int len = SMALLEST_INT.length(); |
| for (int i = 0; i < len; ++i) { |
| b[off++] = (byte) SMALLEST_INT.charAt(i); |
| } |
| return off; |
| } |
| } |