blob: 9c3e04b1ca17df21a7b17d636607f931da126e31 [file] [log] [blame]
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;
}
}