NumberParsingTest.java

package com.fasterxml.jackson.core.read;

import java.io.ByteArrayInputStream;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.exc.InputCoercionException;
import com.fasterxml.jackson.core.io.NumberInput;
import com.fasterxml.jackson.core.json.JsonReadFeature;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Set of basic unit tests for verifying that the basic parser
 * functionality works as expected.
 */
@SuppressWarnings("resource")
class NumberParsingTest
    extends JUnit5TestBase
{
    protected JsonFactory jsonFactory() {
        return sharedStreamFactory();
    }

    private final String ISSUE_4694_VALUE =
        "-11000.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";

    /*
    /**********************************************************************
    /* Tests, Boolean (why here?)
    /**********************************************************************
     */

    @Test
    void simpleBoolean() throws Exception
    {
        _testSimpleBoolean(MODE_INPUT_STREAM);
        _testSimpleBoolean(MODE_INPUT_STREAM_THROTTLED);
        _testSimpleBoolean(MODE_READER);
        _testSimpleBoolean(MODE_DATA_INPUT);
    }

    private void _testSimpleBoolean(int mode) throws Exception
    {
        JsonParser p = createParser(jsonFactory(), mode, "[ true ]");
        assertToken(JsonToken.START_ARRAY, p.nextToken());
        assertToken(JsonToken.VALUE_TRUE, p.nextToken());
        assertTrue(p.getBooleanValue());
        assertToken(JsonToken.END_ARRAY, p.nextToken());
        p.close();
    }

    /*
    /**********************************************************************
    /* Tests, Int
    /**********************************************************************
     */

    @Test
    void simpleInt() throws Exception
    {
        for (int EXP_I : new int[] { 1234, -999, 0, 1, -2, 123456789 }) {
            _testSimpleInt(EXP_I, MODE_INPUT_STREAM);
            _testSimpleInt(EXP_I, MODE_INPUT_STREAM_THROTTLED);
            _testSimpleInt(EXP_I, MODE_READER);
            _testSimpleInt(EXP_I, MODE_DATA_INPUT);
        }
    }

    private void _testSimpleInt(int EXP_I, int mode) throws Exception
    {
        String DOC = "[ "+EXP_I+" ]";
        JsonParser p = createParser(jsonFactory(), mode, DOC);
        assertToken(JsonToken.START_ARRAY, p.nextToken());
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals(JsonParser.NumberType.INT, p.getNumberType());
        assertEquals(JsonParser.NumberTypeFP.UNKNOWN, p.getNumberTypeFP());
        assertTrue(p.isExpectedNumberIntToken());
        assertEquals(""+EXP_I, p.getText());

        if (((short) EXP_I) == EXP_I) {
            assertEquals((short) EXP_I, p.getShortValue());
            if (((byte) EXP_I) == EXP_I) {
                assertEquals((byte) EXP_I, p.getByteValue());
            } else {
                // verify overflow
                try {
                    p.getByteValue();
                    fail("Should get exception for non-byte value "+EXP_I);
                } catch (InputCoercionException e) {
                    verifyException(e, "Numeric value");
                    verifyException(e, "out of range");
                }
            }
        } else {
            // verify overflow
            try {
                p.getShortValue();
                fail("Should get exception for non-short value "+EXP_I);
            } catch (InputCoercionException e) {
                verifyException(e, "Numeric value");
                verifyException(e, "out of range");
            }
        }

        assertEquals(EXP_I, p.getIntValue());
        assertEquals(EXP_I, p.getValueAsInt(EXP_I + 3));
        assertEquals(EXP_I, p.getValueAsInt());
        assertEquals(EXP_I, p.getLongValue());
        assertEquals((double) EXP_I, p.getDoubleValue());
        assertEquals(BigDecimal.valueOf(EXP_I), p.getDecimalValue());
        assertToken(JsonToken.END_ARRAY, p.nextToken());
        p.close();

        DOC = String.valueOf(EXP_I);
        p = createParser(jsonFactory(), mode, DOC + " "); // DataInput requires separator
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertTrue(p.isExpectedNumberIntToken());
        assertEquals(DOC, p.getText());

        int i = p.getIntValue();

        assertEquals(EXP_I, i);
        assertEquals(EXP_I, p.getLongValue());
        assertEquals((double) EXP_I, p.getDoubleValue());
        assertEquals(BigDecimal.valueOf(EXP_I), p.getDecimalValue());
        p.close();

        // and finally, coercion from double to int; couple of variants
        DOC = String.valueOf(EXP_I)+".0";
        p = createParser(jsonFactory(), mode, DOC + " ");
        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
        assertFalse(p.isExpectedNumberIntToken());
        assertEquals(EXP_I, p.getValueAsInt());
        p.close();

        p = createParser(jsonFactory(), mode, DOC + " ");
        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
        assertEquals(EXP_I, p.getValueAsInt(0));
        p.close();
    }

    @Test
    void intRange() throws Exception
    {
        // let's test with readers and streams, separate code paths:
        for (int mode : ALL_MODES) {
            String DOC = "[ "+Integer.MAX_VALUE+","+Integer.MIN_VALUE+" ]";
            JsonParser p = createParser(jsonFactory(), mode, DOC);
            assertToken(JsonToken.START_ARRAY, p.nextToken());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.INT, p.getNumberType());
            assertEquals(Integer.MAX_VALUE, p.getIntValue());

            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.INT, p.getNumberType());
            assertEquals(Integer.MIN_VALUE, p.getIntValue());
            p.close();
        }
    }

    @Test
    void intParsing() throws Exception
    {
        char[] testChars = "123456789".toCharArray();

        assertEquals(3, NumberInput.parseInt(testChars, 2, 1));
        assertEquals(123, NumberInput.parseInt(testChars, 0, 3));
        assertEquals(2345, NumberInput.parseInt(testChars, 1, 4));
        assertEquals(9, NumberInput.parseInt(testChars, 8, 1));
        assertEquals(456789, NumberInput.parseInt(testChars, 3, 6));
        assertEquals(23456, NumberInput.parseInt(testChars, 1, 5));
        assertEquals(123456789, NumberInput.parseInt(testChars, 0, 9));

        testChars = "32".toCharArray();
        assertEquals(32, NumberInput.parseInt(testChars, 0, 2));
        testChars = "189".toCharArray();
        assertEquals(189, NumberInput.parseInt(testChars, 0, 3));

        testChars = "10".toCharArray();
        assertEquals(10, NumberInput.parseInt(testChars, 0, 2));
        assertEquals(0, NumberInput.parseInt(testChars, 1, 1));
    }

    @Test
    void intParsingWithStrings() throws Exception
    {
        assertEquals(3, NumberInput.parseInt("3"));
        assertEquals(3, NumberInput.parseInt("+3"));
        assertEquals(0, NumberInput.parseInt("0"));
        assertEquals(-3, NumberInput.parseInt("-3"));
        assertEquals(27, NumberInput.parseInt("27"));
        assertEquals(-31, NumberInput.parseInt("-31"));
        assertEquals(271, NumberInput.parseInt("271"));
        assertEquals(-131, NumberInput.parseInt("-131"));
        assertEquals(2709, NumberInput.parseInt("2709"));
        assertEquals(-9999, NumberInput.parseInt("-9999"));
        assertEquals(Integer.MIN_VALUE, NumberInput.parseInt(""+Integer.MIN_VALUE));
        assertEquals(Integer.MAX_VALUE, NumberInput.parseInt(""+Integer.MAX_VALUE));
    }

    @Test
    void longParsingWithStrings() throws Exception
    {
        assertEquals(3, NumberInput.parseLong("3"));
        assertEquals(3, NumberInput.parseLong("+3"));
        assertEquals(0, NumberInput.parseLong("0"));
        assertEquals(-3, NumberInput.parseLong("-3"));
        assertEquals(27, NumberInput.parseLong("27"));
        assertEquals(-31, NumberInput.parseLong("-31"));
        assertEquals(271, NumberInput.parseLong("271"));
        assertEquals(-131, NumberInput.parseLong("-131"));
        assertEquals(2709, NumberInput.parseLong("2709"));
        assertEquals(-9999, NumberInput.parseLong("-9999"));
        assertEquals(1234567890123456789L, NumberInput.parseLong("1234567890123456789"));
        assertEquals(-1234567890123456789L, NumberInput.parseLong("-1234567890123456789"));
        assertEquals(Long.MIN_VALUE, NumberInput.parseLong(""+Long.MIN_VALUE));
        assertEquals(Integer.MIN_VALUE-1, NumberInput.parseLong(""+(Integer.MIN_VALUE-1)));
        assertEquals(Long.MAX_VALUE, NumberInput.parseLong(""+Long.MAX_VALUE));
        assertEquals(Integer.MAX_VALUE+1, NumberInput.parseLong(""+(Integer.MAX_VALUE+1)));
    }

    @Test
    void intOverflow() {
        try {
            // Integer.MAX_VALUE + 1
            NumberInput.parseInt("2147483648");
            fail("expected NumberFormatException");
        } catch (NumberFormatException nfe) {
            verifyException(nfe, "For input string: \"2147483648\"");
        }
        try {
            // Integer.MIN_VALUE - 1
            NumberInput.parseInt("-2147483649");
            fail("expected NumberFormatException");
        } catch (NumberFormatException nfe) {
            verifyException(nfe, "For input string: \"-2147483649\"");
        }
    }

    @Test
    void longOverflow() {
        try {
            // Long.MAX_VALUE + 1
            NumberInput.parseLong("9223372036854775808");
            fail("expected NumberFormatException");
        } catch (NumberFormatException nfe) {
            verifyException(nfe, "For input string: \"9223372036854775808\"");
        }
        try {
            // Long.MIN_VALUE - 1
            NumberInput.parseLong("-9223372036854775809");
            fail("expected NumberFormatException");
        } catch (NumberFormatException nfe) {
            verifyException(nfe, "For input string: \"-9223372036854775809\"");
        }
    }

    // Found by oss-fuzzer
    @Test
    void veryLongIntRootValue() throws Exception
    {
        // For some reason running multiple will tend to hide the issue;
        // possibly due to re-use of some buffers
        _testVeryLongIntRootValue(newStreamFactory(), MODE_DATA_INPUT);
    }

    private void _testVeryLongIntRootValue(JsonFactory jsonF, int mode) throws Exception
    {
        StringBuilder sb = new StringBuilder(250);
        sb.append("-2");
        for (int i = 0; i < 220; ++i) {
            sb.append('0');
        }
        sb.append(' '); // mostly important for DataInput
        String DOC = sb.toString();

        try (JsonParser p = createParser(jsonF, mode, DOC)) {
            assertToken(p.nextToken(), JsonToken.VALUE_NUMBER_INT);
            assertNotNull(p.getBigIntegerValue());
        }
    }

    /*
    /**********************************************************************
    /* Tests, Long
    /**********************************************************************
     */

    @Test
    void simpleLong() throws Exception
    {
        _testSimpleLong(MODE_INPUT_STREAM);
        _testSimpleLong(MODE_INPUT_STREAM_THROTTLED);
        _testSimpleLong(MODE_READER);
        _testSimpleLong(MODE_DATA_INPUT);
    }

    private void _testSimpleLong(int mode) throws Exception
    {
        long EXP_L = 12345678907L;

        JsonParser p = createParser(jsonFactory(), mode, "[ "+EXP_L+" ]");
        assertToken(JsonToken.START_ARRAY, p.nextToken());
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        // beyond int, should be long
        assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
        assertEquals(""+EXP_L, p.getText());

        assertEquals(EXP_L, p.getLongValue());
        // Should get an exception if trying to convert to int
        try {
            p.getIntValue();
        } catch (InputCoercionException e) {
            verifyException(e, "out of range");
            assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType());
            assertEquals(Integer.TYPE, e.getTargetType());
        }
        assertEquals((double) EXP_L, p.getDoubleValue());
        assertEquals(BigDecimal.valueOf(EXP_L), p.getDecimalValue());
        p.close();
    }

    @Test
    void longRange() throws Exception
    {
        for (int mode : ALL_MODES) {
            long belowMinInt = -1L + Integer.MIN_VALUE;
            long aboveMaxInt = 1L + Integer.MAX_VALUE;
            long belowMaxLong = -1L + Long.MAX_VALUE;
            long aboveMinLong = 1L + Long.MIN_VALUE;
            String input = "[ "+Long.MAX_VALUE+","+Long.MIN_VALUE+","+aboveMaxInt+", "+belowMinInt+","+belowMaxLong+", "+aboveMinLong+" ]";
            JsonParser p = createParser(jsonFactory(), mode, input);
            assertToken(JsonToken.START_ARRAY, p.nextToken());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
            assertEquals(Long.MAX_VALUE, p.getLongValue());

            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
            assertEquals(Long.MIN_VALUE, p.getLongValue());

            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
            assertEquals(aboveMaxInt, p.getLongValue());

            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
            assertEquals(belowMinInt, p.getLongValue());

            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
            assertEquals(belowMaxLong, p.getLongValue());

            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
            assertEquals(aboveMinLong, p.getLongValue());


            assertToken(JsonToken.END_ARRAY, p.nextToken());
            p.close();
        }
    }

    @Test
    void longParsing() throws Exception
    {
        char[] testChars = "123456789012345678".toCharArray();

        assertEquals(123456789012345678L, NumberInput.parseLong(testChars, 0, testChars.length));
    }

    @Test
    void longBoundsChecks() throws Exception
    {
        String minLong = String.valueOf(Long.MIN_VALUE).substring(1);
        String belowMinLong = BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE)
                .toString().substring(1);
        String maxLong = String.valueOf(Long.MAX_VALUE);
        String aboveMaxLong = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE).toString();
        final String VALUE_491 = "1323372036854775807"; // is within range (JACKSON-491)
        final String OVERFLOW =  "9999999999999999999"; // and this one is clearly out

        assertTrue(NumberInput.inLongRange(minLong, true));
        assertTrue(NumberInput.inLongRange(maxLong, false));
        assertTrue(NumberInput.inLongRange(VALUE_491, true));
        assertTrue(NumberInput.inLongRange(VALUE_491, false));

        assertFalse(NumberInput.inLongRange(OVERFLOW, false));
        assertFalse(NumberInput.inLongRange(OVERFLOW, true));
        assertFalse(NumberInput.inLongRange(belowMinLong, true));
        assertFalse(NumberInput.inLongRange(aboveMaxLong, false));

        char[] cbuf = minLong.toCharArray();
        assertTrue(NumberInput.inLongRange(cbuf, 0, cbuf.length, true));
        cbuf = maxLong.toCharArray();
        assertTrue(NumberInput.inLongRange(cbuf, 0, cbuf.length, false));
        cbuf = VALUE_491.toCharArray();
        assertTrue(NumberInput.inLongRange(cbuf, 0, cbuf.length, true));
        assertTrue(NumberInput.inLongRange(cbuf, 0, cbuf.length, false));

        cbuf = OVERFLOW.toCharArray();
        assertFalse(NumberInput.inLongRange(cbuf, 0, cbuf.length, true));
        assertFalse(NumberInput.inLongRange(cbuf, 0, cbuf.length, false));

        cbuf = aboveMaxLong.toCharArray();
        assertFalse(NumberInput.inLongRange(cbuf, 0, cbuf.length, false));
    }

    /*
    /**********************************************************************
    /* Tests, BigXxx
    /**********************************************************************
     */

    @Test
    void bigDecimalRange() throws Exception
    {
        for (int mode : ALL_MODES) {
            // let's test first values outside of Long range
            BigInteger small = new BigDecimal(Long.MIN_VALUE).toBigInteger();
            small = small.subtract(BigInteger.ONE);
            BigInteger big = new BigDecimal(Long.MAX_VALUE).toBigInteger();
            big = big.add(BigInteger.ONE);
            String input = "[ "+small+"  ,  "+big+"]";
            JsonParser p = createParser(jsonFactory(), mode, input);
            assertToken(JsonToken.START_ARRAY, p.nextToken());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.BIG_INTEGER, p.getNumberType());
            assertEquals(small, p.getBigIntegerValue());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.BIG_INTEGER, p.getNumberType());
            assertEquals(big, p.getBigIntegerValue());
            assertToken(JsonToken.END_ARRAY, p.nextToken());
            p.close();
        }
    }

    // for [core#78]
    @Test
    void bigNumbers() throws Exception
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 520; ++i) { // input buffer is 512 bytes by default
            sb.append('1');
        }
        final String NUMBER_STR = sb.toString();
        BigInteger biggie = new BigInteger(NUMBER_STR);

        for (int mode : ALL_MODES) {
            try (JsonParser p = createParser(jsonFactory(), mode, NUMBER_STR + " ")) {
                assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
                assertEquals(JsonParser.NumberType.BIG_INTEGER, p.getNumberType());
                assertEquals(NUMBER_STR, p.getText());
                assertEquals(biggie, p.getBigIntegerValue());
            }
        }
    }

    @Test
    void intsWith19Chars() throws Exception
    {
        final String[] values = new String[] {
            "9223372036854775808", "9999999999999999999"
        };
        for (String value : values) {
            BigInteger biggie = new BigInteger(value);

            for (int mode : ALL_MODES) {
                try (JsonParser p = createParser(jsonFactory(), mode, value + " ")) {
                    assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
                    assertEquals(JsonParser.NumberType.BIG_INTEGER, p.getNumberType());
                    assertEquals(biggie, p.getBigIntegerValue());
                    assertEquals(value, p.getText());
                }
            }
        }
    }

    // Related to [core#1135]: JsonParser.isNaN() should not be fooled
    // by possible Double overflow
    @Test
    void biggerThanFloatHandling() throws Exception
    {
        BigDecimal tooBig = BigDecimal.valueOf(Double.MAX_VALUE);
        tooBig = tooBig.add(tooBig);
        // Otherwise becomes an int, need to add fractional part
        tooBig = tooBig.add(BigDecimal.valueOf(0.25));
        String tooBigString = tooBig.toPlainString();

        // sanity check that it is beyond Double range:
        assertTrue(Double.valueOf(tooBigString).isInfinite());

        for (int mode : ALL_MODES) {
            try (JsonParser p = createParser(jsonFactory(), mode, tooBigString +" ")) {
                assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                // NOTE! We MUST NOT call `p.getNumberType()` as that would make
                // it `NumberType.DOUBLE`
//                assertEquals(JsonParser.NumberType.BIG_DECIMAL, p.getNumberType());
                assertFalse(p.isNaN());
                assertEquals(tooBigString, p.getText());
                assertEquals(tooBig, p.getDecimalValue());
                assertFalse(p.isNaN());
            }
        }
    }

    /*
    /**********************************************************************
    /* Tests, int/long/BigInteger via E-notation (engineering)
    /**********************************************************************
     */
    
    @Test
    void bigIntegerWithENotation() throws Exception {
        final String DOC = "1e5 ";
        for (int mode : ALL_MODES) {
            try (JsonParser p = createParser(jsonFactory(), mode, DOC)) {
                assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                assertEquals(100000L, p.getBigIntegerValue().longValue());
            }
        }
    }

    @Test
    void longWithENotation() throws Exception {
        final String DOC = "1e5 ";
        for (int mode : ALL_MODES) {
            try (JsonParser p = createParser(jsonFactory(), mode, DOC)) {
                assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                assertEquals(100000L, p.getLongValue());
            }
        }
    }

    @Test
    void intWithENotation() throws Exception {
        final String DOC = "1e5 ";
        for (int mode : ALL_MODES) {
            try (JsonParser p = createParser(jsonFactory(), mode, DOC)) {
                assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                assertEquals(100000, p.getIntValue());
            }
        }
    }

    @Test
    void largeBigIntegerWithENotation() throws Exception {
        // slightly bigger than Double.MAX_VALUE (we need to avoid parsing as double in this case)
        final String DOC = "2e308 ";
        final BigInteger expected = new BigDecimal(DOC.trim()).toBigInteger();
        for (int mode : ALL_MODES) {
            try (JsonParser p = createParser(jsonFactory(), mode, DOC)) {
                assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                assertEquals(expected, p.getBigIntegerValue());
            }
        }
    }

    /*
    /**********************************************************************
    /* Tests, floating point (basic)
    /**********************************************************************
     */

    @Test
    void simpleDouble() throws Exception
    {
        final String[] INPUTS = new String[] {
            "1234.00", "2.1101567E-16", "1.0e5", "0.0", "1.0", "-1.0",
            "-0.5", "-12.9", "-999.0",
            "2.5e+5", "9e4", "-12e-3", "0.25",
        };
        for (int mode : ALL_MODES) {
            for (int i = 0; i < INPUTS.length; ++i) {

                // First in array

                String STR = INPUTS[i];
                double EXP_D = Double.parseDouble(STR);
                String DOC = "["+STR+"]";

                JsonParser p = createParser(jsonFactory(), mode, DOC+" ");
                assertToken(JsonToken.START_ARRAY, p.nextToken());

                assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                assertEquals(STR, p.getText());
                assertEquals(EXP_D, p.getDoubleValue());
                assertToken(JsonToken.END_ARRAY, p.nextToken());
                if (mode != MODE_DATA_INPUT) {
                    assertNull(p.nextToken());
                }
                p.close();

                // then outside
                p = createParser(jsonFactory(), mode, STR + " ");
                JsonToken t = null;

                try {
                    t = p.nextToken();
                } catch (Exception e) {
                    throw new Exception("Failed to parse input '"+STR+"' (parser of type "+p.getClass().getSimpleName()+")", e);
                }

                assertToken(JsonToken.VALUE_NUMBER_FLOAT, t);
                assertEquals(STR, p.getText());
                if (mode != MODE_DATA_INPUT) {
                    assertNull(p.nextToken());
                }
                p.close();
            }
        }
    }

    @Test
    void floatBoundary146Chars() throws Exception
    {
        final char[] arr = new char[50005];
        for(int i = 500; i != 9000; ++i) {
          java.util.Arrays.fill(arr, 0, i, ' ');
          arr[i] = '-';
          arr[i + 1] = '1';
          arr[i + 2] = 'e';
          arr[i + 3] = '-';
          arr[i + 4] = '1';
          CharArrayReader r = new CharArrayReader(arr, 0, i+5);
          JsonParser p = jsonFactory().createParser(r);
          assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
          p.close();
        }
    }

    @Test
    void floatBoundary146Bytes() throws Exception
    {
        final byte[] arr = new byte[50005];
        for(int i = 500; i != 9000; ++i) {
            java.util.Arrays.fill(arr, 0, i, (byte) 0x20);
            arr[i] = '-';
            arr[i + 1] = '1';
            arr[i + 2] = 'e';
            arr[i + 3] = '-';
            arr[i + 4] = '1';
            ByteArrayInputStream in = new ByteArrayInputStream(arr, 0, i+5);
            JsonParser p = jsonFactory().createParser(in);
            assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
            p.close();
        }
    }

    /*
    /**********************************************************************
    /* Tests, misc other
    /**********************************************************************
     */

    @Test
    void numbers() throws Exception
    {
        _testNumbers(MODE_INPUT_STREAM);
        _testNumbers(MODE_INPUT_STREAM_THROTTLED);
        _testNumbers(MODE_READER);
        _testNumbers(MODE_DATA_INPUT);
    }

    private void _testNumbers(int mode) throws Exception
    {
        final String DOC = "[ -13, 8100200300, 13.5, 0.00010, -2.033 ]";

        JsonParser p = createParser(jsonFactory(), mode, DOC);

        assertToken(JsonToken.START_ARRAY, p.nextToken());

        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals(-13, p.getIntValue());
        assertEquals(-13L, p.getLongValue());
        assertEquals(-13., p.getDoubleValue());
        assertEquals("-13", p.getText());

        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals(8100200300L, p.getLongValue());
        // Should get exception for overflow:
        try {
            /*int x =*/ p.getIntValue();
            fail("Expected an exception for overflow");
        } catch (Exception e) {
            verifyException(e, "out of range of int");
        }
        assertEquals(8100200300.0, p.getDoubleValue());
        assertEquals("8100200300", p.getText());

        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
        assertEquals(13, p.getIntValue());
        assertEquals(13L, p.getLongValue());
        assertEquals(13.5, p.getDoubleValue());
        assertEquals("13.5", p.getText());

        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
        assertEquals(0, p.getIntValue());
        assertEquals(0L, p.getLongValue());
        assertEquals(0.00010, p.getDoubleValue());
        assertEquals("0.00010", p.getText());

        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
        assertEquals(-2, p.getIntValue());
        assertEquals(-2L, p.getLongValue());
        assertEquals(-2.033, p.getDoubleValue());
        assertEquals("-2.033", p.getText());

        assertToken(JsonToken.END_ARRAY, p.nextToken());

        p.close();
    }


    /**
     * Method that tries to test that number parsing works in cases where
     * input is split between buffer boundaries.
     */
    @Test
    void parsingOfLongerSequences() throws Exception
    {
        double[] values = new double[] { 0.01, -10.5, 2.1e9, 4.0e-8 };
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < values.length; ++i) {
            if (i > 0) {
                sb.append(',');
            }
            sb.append(values[i]);
        }
        String segment = sb.toString();

        int COUNT = 1000;
        sb = new StringBuilder(COUNT * segment.length() + 20);
        sb.append("[");
        for (int i = 0; i < COUNT; ++i) {
            if (i > 0) {
                sb.append(',');
            }
            sb.append(segment);
            sb.append('\n');
            // let's add somewhat arbitrary number of spaces
            int x = (i & 3);
            if (i > 300) {
                x += i % 5;
            }
            while (--x > 0) {
                sb.append(' ');
            }
        }
        sb.append("]");
        String DOC = sb.toString();

        for (int input = 0; input < 2; ++input) {
            JsonParser p;

            if (input == 0) {
                p = createParserUsingStream(jsonFactory(), DOC, "UTF-8");
            } else {
                p = jsonFactory().createParser(DOC);
            }

            assertToken(JsonToken.START_ARRAY, p.nextToken());
            for (int i = 0; i < COUNT; ++i) {
                for (double d : values) {
                    assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                    assertEquals(d, p.getDoubleValue());
                }
            }
            assertToken(JsonToken.END_ARRAY, p.nextToken());
            p.close();
        }
    }

    // [jackson-core#157]
    // 19-Dec-2022, tatu: Reduce length so as not to hit too-long-number limit
    @Test
    void longNumbers() throws Exception
    {
        StringBuilder sb = new StringBuilder(900);
        for (int i = 0; i < 900; ++i) {
            sb.append('9');
        }
        String NUM = sb.toString();
        // force use of new factory, just in case (might still recycle same buffers tho?)
        JsonFactory f = new JsonFactory();
        _testLongNumbers(f, NUM, false);
        _testLongNumbers(f, NUM, true);
    }

    private void _testLongNumbers(JsonFactory f, String num, boolean useStream) throws Exception
    {
        final String doc = "[ "+num+" ]";
        try (JsonParser p = useStream
                ? f.createParser(doc.getBytes("UTF-8"))
                        : f.createParser(doc)) {
            assertToken(JsonToken.START_ARRAY, p.nextToken());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(num, p.getText());
            assertToken(JsonToken.END_ARRAY, p.nextToken());
        }
    }

    // and alternate take on for #157 (with negative num)
    @Test
    void longNumbers2() throws Exception
    {
        StringBuilder input = new StringBuilder();
        // test this with negative
        input.append('-');
        for (int i = 0; i < 2100; i++) {
            input.append(1);
        }
        final String DOC = input.toString();
        JsonFactory f = JsonFactory.builder()
                .streamReadConstraints(StreamReadConstraints.builder().maxNumberLength(10000).build())
                .build();
        _testIssue160LongNumbers(f, DOC, false);
        _testIssue160LongNumbers(f, DOC, true);
    }

    private void _testIssue160LongNumbers(JsonFactory f, String doc, boolean useStream) throws Exception
    {
        JsonParser p = useStream
                ? f.createParser(doc.getBytes("UTF-8"))
                : f.createParser(doc);
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        BigInteger v = p.getBigIntegerValue();
        assertNull(p.nextToken());
        assertEquals(doc, v.toString());
    }

    // for [jackson-core#181]
    /**
     * Method that tries to test that number parsing works in cases where
     * input is split between buffer boundaries.
     */
    @Test
    void parsingOfLongerSequencesWithNonNumeric() throws Exception
    {
        JsonFactory f = JsonFactory.builder()
                .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS)
                .build();
        _testParsingOfLongerSequencesWithNonNumeric(f, MODE_INPUT_STREAM);
        _testParsingOfLongerSequencesWithNonNumeric(f, MODE_INPUT_STREAM_THROTTLED);
        _testParsingOfLongerSequencesWithNonNumeric(f, MODE_READER);
        _testParsingOfLongerSequencesWithNonNumeric(f, MODE_DATA_INPUT);
    }

    private void _testParsingOfLongerSequencesWithNonNumeric(JsonFactory f, int mode) throws Exception
    {
        double[] values = new double[] {
                0.01, -10.5, 2.1e9, 4.0e-8,
                Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY
        };
        for (int i = 0; i < values.length; ++i) {
            int COUNT = 4096;
            // Don't see the failure with a multiple of 1
            int VCOUNT = 2 * COUNT;
            String arrayJson = toJsonArray(values[i], VCOUNT);
            StringBuilder sb = new StringBuilder(COUNT + arrayJson.length() + 20);
            for (int j = 0; j < COUNT; ++j) {
                sb.append(' ');
            }
            sb.append(arrayJson);
            String DOC = sb.toString();
            for (int input = 0; input < 2; ++input) {
                JsonParser p = createParser(f, mode, DOC);
                assertToken(JsonToken.START_ARRAY, p.nextToken());
                for (int j = 0; j < VCOUNT; ++j) {
                    assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                    double exp = values[i];
                    double act = p.getDoubleValue();
                    if (Double.compare(exp, act) != 0) {
                        fail("Expected at #"+j+" value "+exp+", instead got "+act);
                    }
                    if (Double.isNaN(exp) || Double.isInfinite(exp)) {
                        assertTrue(p.isNaN());
                    } else {
                        assertFalse(p.isNaN());
                    }
                }
                assertToken(JsonToken.END_ARRAY, p.nextToken());
                p.close();
            }
        }
    }

    /*
    /**********************************************************
    /* Tests for invalid access
    /**********************************************************
     */

    @Test
    void invalidBooleanAccess() throws Exception {
        _testInvalidBooleanAccess(MODE_INPUT_STREAM);
        _testInvalidBooleanAccess(MODE_INPUT_STREAM_THROTTLED);
        _testInvalidBooleanAccess(MODE_READER);
        _testInvalidBooleanAccess(MODE_DATA_INPUT);
    }

    private void _testInvalidBooleanAccess(int mode) throws Exception
    {
        JsonParser p = createParser(jsonFactory(), mode, "[ \"abc\" ]");
        assertToken(JsonToken.START_ARRAY, p.nextToken());
        assertToken(JsonToken.VALUE_STRING, p.nextToken());
        try {
            p.getBooleanValue();
            fail("Expected error trying to call getBooleanValue on non-boolean value");
        } catch (JsonParseException e) {
            verifyException(e, "not of boolean type");
        }
        p.close();
    }

    @Test
    void invalidIntAccess() throws Exception {
        _testInvalidIntAccess(MODE_INPUT_STREAM);
        _testInvalidIntAccess(MODE_INPUT_STREAM_THROTTLED);
        _testInvalidIntAccess(MODE_READER);
        _testInvalidIntAccess(MODE_DATA_INPUT);
    }

    private void _testInvalidIntAccess(int mode) throws Exception
    {
        JsonParser p = createParser(jsonFactory(), mode, "[ \"abc\" ]");
        assertToken(JsonToken.START_ARRAY, p.nextToken());
        assertToken(JsonToken.VALUE_STRING, p.nextToken());
        try {
            p.getIntValue();
            fail("Expected error trying to call getIntValue on non-numeric value");
        } catch (JsonParseException e) {
            verifyException(e, "can not use numeric value accessors");
        }
        p.close();
    }

    @Test
    void invalidLongAccess() throws Exception {
        _testInvalidLongAccess(MODE_INPUT_STREAM);
        _testInvalidLongAccess(MODE_INPUT_STREAM_THROTTLED);
        _testInvalidLongAccess(MODE_READER);
        _testInvalidLongAccess(MODE_DATA_INPUT);
    }

    private void _testInvalidLongAccess(int mode) throws Exception
    {
        JsonParser p = createParser(jsonFactory(), mode, "[ false ]");
        assertToken(JsonToken.START_ARRAY, p.nextToken());
        assertToken(JsonToken.VALUE_FALSE, p.nextToken());
        try {
            p.getLongValue();
            fail("Expected error trying to call getLongValue on non-numeric value");
        } catch (JsonParseException e) {
            verifyException(e, "can not use numeric value accessors");
        }
        p.close();
    }

    // [core#317]
    @Test
    void longerFloatingPoint() throws Exception
    {
        StringBuilder input = new StringBuilder();
        for (int i = 1; i < 201; i++) {
            input.append(1);
        }
        input.append(".0");
        final String DOC = input.toString();

        // test out with both Reader and ByteArrayInputStream
        JsonParser p;

        p = jsonFactory().createParser(new StringReader(DOC));
        _testLongerFloat(p, DOC);
        p.close();

        p = jsonFactory().createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")));
        _testLongerFloat(p, DOC);
        p.close();
    }

    private void _testLongerFloat(JsonParser p, String text) throws IOException
    {
        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
        assertEquals(text, p.getText());
        assertNull(p.nextToken());
    }

    @Test
    void invalidNumber() throws Exception {
        for (int mode : ALL_MODES) {
            JsonParser p = createParser(jsonFactory(), mode, " -foo ");
            try {
                p.nextToken();
                fail("Should not pass");
            } catch (JsonParseException e) {
                verifyException(e, "Unexpected character ('f'");
            }
            p.close();
        }
    }

    @Test
    void negativeMaxNumberLength() {
        try {
            StreamReadConstraints src = StreamReadConstraints.builder().maxNumberLength(-1).build();
            fail("expected IllegalArgumentException; instead built: "+src);
        } catch (IllegalArgumentException iae) {
            verifyException(iae, "Cannot set maxNumberLength to a negative value");
        }
    }

    // https://github.com/FasterXML/jackson-databind/issues/4694
    @Test
    void databind4694() throws Exception {
        final BigDecimal expected = new BigDecimal(ISSUE_4694_VALUE);
        for (int mode : ALL_MODES) {
            try (JsonParser p = createParser(mode, String.format(" %s ", ISSUE_4694_VALUE))) {
                assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                assertEquals(expected, p.getDecimalValue());
                assertFalse(p.isNaN());
            }
        }
    }

    void databind4694Double() throws Exception {
        final Double expected = Double.valueOf(ISSUE_4694_VALUE);
        for (int mode : ALL_MODES) {
            try (JsonParser p = createParser(mode, String.format(" %s ", ISSUE_4694_VALUE))) {
                assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                assertEquals(expected, p.getDoubleValue());
                assertFalse(p.isNaN());
            }
        }
    }

    void databind4694Float() throws Exception {
        final Float expected = Float.valueOf(ISSUE_4694_VALUE);
        for (int mode : ALL_MODES) {
            try (JsonParser p = createParser(mode, String.format(" %s ", ISSUE_4694_VALUE))) {
                assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
                assertEquals(expected, p.getFloatValue());
                assertFalse(p.isNaN());
            }
        }
    }

    /*
    /**********************************************************
    /* Helper methods
    /**********************************************************
     */

    private String toJsonArray(double v, int n) {
        StringBuilder sb = new StringBuilder().append('[').append(v);
        for (int i = 1; i < n; ++i) {
            sb.append(',').append(v);
        }
        return sb.append(']').toString();
    }
}