NumberInputTest.java
package tools.jackson.core.unittest.io;
import java.math.BigInteger;
import org.junit.jupiter.api.Test;
import tools.jackson.core.io.NumberInput;
import tools.jackson.core.unittest.JacksonCoreTestBase;
import static org.junit.jupiter.api.Assertions.*;
class NumberInputTest
extends JacksonCoreTestBase
{
@Test
void nastySmallDouble()
{
//relates to https://github.com/FasterXML/jackson-core/issues/750
//prior to jackson v2.14, this value used to be returned as Double.MIN_VALUE
final String nastySmallDouble = "2.2250738585072012e-308";
assertEquals(Double.parseDouble(nastySmallDouble), NumberInput.parseDouble(nastySmallDouble, false));
assertEquals(Double.parseDouble(nastySmallDouble), NumberInput.parseDouble(nastySmallDouble, true));
}
@Test
void parseFloat()
{
final String exampleFloat = "1.199999988079071";
assertEquals(1.1999999f, NumberInput.parseFloat(exampleFloat, false));
assertEquals(1.1999999f, NumberInput.parseFloat(exampleFloat, true));
assertEquals(1.2f, (float)NumberInput.parseDouble(exampleFloat, false));
assertEquals(1.2f, (float)NumberInput.parseDouble(exampleFloat, true));
final String exampleFloat2 = "7.006492321624086e-46";
assertEquals("1.4E-45", Float.toString(NumberInput.parseFloat(exampleFloat2, false)));
assertEquals("1.4E-45", Float.toString(NumberInput.parseFloat(exampleFloat2, true)));
}
@Test
void parseLongBigInteger()
{
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 1000; i++) {
stringBuilder.append(7);
}
String test1000 = stringBuilder.toString();
assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000, false));
assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000, true));
for (int i = 0; i < 1000; i++) {
stringBuilder.append(7);
}
String test2000 = stringBuilder.toString();
assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000, false));
assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000, true));
}
@Test
void bigIntegerWithRadix()
{
final String val = "1ABCDEF";
final int radix = 16;
BigInteger expected = new BigInteger(val, radix);
assertEquals(expected, NumberInput.parseBigIntegerWithRadix(val, radix, true));
assertEquals(expected, NumberInput.parseBigIntegerWithRadix(val, radix, false));
}
@Test
void parseBigIntegerFailsWithENotation()
{
try {
NumberInput.parseBigInteger("1e10", false);
fail("expected NumberFormatException");
} catch (NumberFormatException e) {
verifyException(e, "1e10");
}
}
@Test
void looksLikeValidNumberTrue()
{
assertTrue(NumberInput.looksLikeValidNumber("0"));
assertTrue(NumberInput.looksLikeValidNumber("1"));
assertTrue(NumberInput.looksLikeValidNumber("-1"));
assertTrue(NumberInput.looksLikeValidNumber("+1")); // non-JSON
assertTrue(NumberInput.looksLikeValidNumber("0001")); // non-JSON
// https://github.com/FasterXML/jackson-databind/issues/4435
assertTrue(NumberInput.looksLikeValidNumber(".0"));
assertTrue(NumberInput.looksLikeValidNumber("-.0"));
assertTrue(NumberInput.looksLikeValidNumber("+.0"));
assertTrue(NumberInput.looksLikeValidNumber(".01"));
assertTrue(NumberInput.looksLikeValidNumber("-.01"));
assertTrue(NumberInput.looksLikeValidNumber("+.01"));
assertTrue(NumberInput.looksLikeValidNumber("0.01"));
assertTrue(NumberInput.looksLikeValidNumber("-0.10"));
assertTrue(NumberInput.looksLikeValidNumber("+0.25")); // non-JSON
assertTrue(NumberInput.looksLikeValidNumber("10.33"));
assertTrue(NumberInput.looksLikeValidNumber("-1.39"));
assertTrue(NumberInput.looksLikeValidNumber("+125.0")); // non-JSON
assertTrue(NumberInput.looksLikeValidNumber("1E10"));
assertTrue(NumberInput.looksLikeValidNumber("-1E10"));
assertTrue(NumberInput.looksLikeValidNumber("1e-10"));
assertTrue(NumberInput.looksLikeValidNumber("1e+10"));
assertTrue(NumberInput.looksLikeValidNumber("+1e+10"));
assertTrue(NumberInput.looksLikeValidNumber("1.4E-45"));
assertTrue(NumberInput.looksLikeValidNumber("1.4e+45"));
// https://github.com/FasterXML/jackson-core/issues/1308
assertTrue(NumberInput.looksLikeValidNumber("0."));
assertTrue(NumberInput.looksLikeValidNumber("6."));
assertTrue(NumberInput.looksLikeValidNumber("65."));
assertTrue(NumberInput.looksLikeValidNumber("654."));
assertTrue(NumberInput.looksLikeValidNumber("65432."));
assertTrue(NumberInput.looksLikeValidNumber("-0."));
assertTrue(NumberInput.looksLikeValidNumber("-6."));
assertTrue(NumberInput.looksLikeValidNumber("-65."));
assertTrue(NumberInput.looksLikeValidNumber("-654."));
assertTrue(NumberInput.looksLikeValidNumber("-65432."));
assertTrue(NumberInput.looksLikeValidNumber("+0."));
assertTrue(NumberInput.looksLikeValidNumber("+6."));
assertTrue(NumberInput.looksLikeValidNumber("+65."));
assertTrue(NumberInput.looksLikeValidNumber("+654."));
assertTrue(NumberInput.looksLikeValidNumber("+65432."));
}
@Test
void looksLikeValidNumberFalse()
{
// https://github.com/FasterXML/jackson-databind/issues/4435 and
// https://github.com/FasterXML/jackson-core/issues/1308
assertFalse(NumberInput.looksLikeValidNumber(""));
assertFalse(NumberInput.looksLikeValidNumber(" "));
assertFalse(NumberInput.looksLikeValidNumber(" "));
assertFalse(NumberInput.looksLikeValidNumber("."));
assertFalse(NumberInput.looksLikeValidNumber("10_000"));
assertFalse(NumberInput.looksLikeValidNumber("-"));
assertFalse(NumberInput.looksLikeValidNumber("+"));
assertFalse(NumberInput.looksLikeValidNumber("-."));
assertFalse(NumberInput.looksLikeValidNumber("+."));
assertFalse(NumberInput.looksLikeValidNumber("-E"));
assertFalse(NumberInput.looksLikeValidNumber("+E"));
}
@Test
void parseIntFromCharArray()
{
// Test parsing integers from char arrays with various lengths
// Single digit
char[] ch1 = "5".toCharArray();
assertEquals(5, NumberInput.parseInt(ch1, 0, 1));
// Two digits
char[] ch2 = "42".toCharArray();
assertEquals(42, NumberInput.parseInt(ch2, 0, 2));
// Three digits
char[] ch3 = "123".toCharArray();
assertEquals(123, NumberInput.parseInt(ch3, 0, 3));
// Four digits
char[] ch4 = "1234".toCharArray();
assertEquals(1234, NumberInput.parseInt(ch4, 0, 4));
// Five digits
char[] ch5 = "12345".toCharArray();
assertEquals(12345, NumberInput.parseInt(ch5, 0, 5));
// Six digits
char[] ch6 = "123456".toCharArray();
assertEquals(123456, NumberInput.parseInt(ch6, 0, 6));
// Seven digits
char[] ch7 = "1234567".toCharArray();
assertEquals(1234567, NumberInput.parseInt(ch7, 0, 7));
// Eight digits
char[] ch8 = "12345678".toCharArray();
assertEquals(12345678, NumberInput.parseInt(ch8, 0, 8));
// Nine digits (max for fast path)
char[] ch9 = "123456789".toCharArray();
assertEquals(123456789, NumberInput.parseInt(ch9, 0, 9));
// Test with offset
char[] chOffset = "abc123def".toCharArray();
assertEquals(123, NumberInput.parseInt(chOffset, 3, 3));
// Test with leading plus sign
char[] chPlus = "+42".toCharArray();
assertEquals(42, NumberInput.parseInt(chPlus, 0, 3));
}
@Test
void parseIntFromString()
{
// Positive numbers
assertEquals(0, NumberInput.parseInt("0"));
assertEquals(5, NumberInput.parseInt("5"));
assertEquals(42, NumberInput.parseInt("42"));
assertEquals(123, NumberInput.parseInt("123"));
assertEquals(999999999, NumberInput.parseInt("999999999"));
// Negative numbers
assertEquals(-1, NumberInput.parseInt("-1"));
assertEquals(-42, NumberInput.parseInt("-42"));
assertEquals(-123, NumberInput.parseInt("-123"));
assertEquals(-999999999, NumberInput.parseInt("-999999999"));
// Boundary values
assertEquals(Integer.MAX_VALUE, NumberInput.parseInt(String.valueOf(Integer.MAX_VALUE)));
assertEquals(Integer.MIN_VALUE, NumberInput.parseInt(String.valueOf(Integer.MIN_VALUE)));
}
@Test
void parseLongFromCharArray()
{
// Test 10 digit number
char[] ch10 = "1234567890".toCharArray();
assertEquals(1234567890L, NumberInput.parseLong(ch10, 0, 10));
// Test 15 digit number
char[] ch15 = "123456789012345".toCharArray();
assertEquals(123456789012345L, NumberInput.parseLong(ch15, 0, 15));
// Test 18 digit number
char[] ch18 = "123456789012345678".toCharArray();
assertEquals(123456789012345678L, NumberInput.parseLong(ch18, 0, 18));
}
@Test
void parseLong19Digits()
{
// Test parsing exactly 19 digit numbers
char[] ch19 = "1234567890123456789".toCharArray();
assertEquals(1234567890123456789L, NumberInput.parseLong19(ch19, 0, false));
assertEquals(-1234567890123456789L, NumberInput.parseLong19(ch19, 0, true));
// Test with offset
char[] chOffset = "xxx9223372036854775807".toCharArray();
assertEquals(9223372036854775807L, NumberInput.parseLong19(chOffset, 3, false));
}
@Test
void parseLongFromString()
{
// Short values (delegates to parseInt)
assertEquals(0L, NumberInput.parseLong("0"));
assertEquals(123L, NumberInput.parseLong("123"));
assertEquals(-456L, NumberInput.parseLong("-456"));
// Long values
assertEquals(12345678901L, NumberInput.parseLong("12345678901"));
assertEquals(-12345678901L, NumberInput.parseLong("-12345678901"));
// Boundary values
assertEquals(Long.MAX_VALUE, NumberInput.parseLong(String.valueOf(Long.MAX_VALUE)));
assertEquals(Long.MIN_VALUE, NumberInput.parseLong(String.valueOf(Long.MIN_VALUE)));
}
@Test
void inLongRangeFromCharArray()
{
// Values clearly in range
char[] small = "12345".toCharArray();
assertTrue(NumberInput.inLongRange(small, 0, 5, false));
assertTrue(NumberInput.inLongRange(small, 0, 5, true));
// Boundary testing - Long.MAX_VALUE = 9223372036854775807 (19 digits)
char[] maxLong = "9223372036854775807".toCharArray();
assertTrue(NumberInput.inLongRange(maxLong, 0, 19, false));
// Just above Long.MAX_VALUE
char[] aboveMax = "9223372036854775808".toCharArray();
assertFalse(NumberInput.inLongRange(aboveMax, 0, 19, false));
// Long.MIN_VALUE = -9223372036854775808 (19 digits without sign)
char[] minLong = "9223372036854775808".toCharArray();
assertTrue(NumberInput.inLongRange(minLong, 0, 19, true));
// Just above Long.MIN_VALUE magnitude
char[] aboveMin = "9223372036854775809".toCharArray();
assertFalse(NumberInput.inLongRange(aboveMin, 0, 19, true));
// 20 digits - always too large
char[] tooLong = "12345678901234567890".toCharArray();
assertFalse(NumberInput.inLongRange(tooLong, 0, 20, false));
}
@Test
void inLongRangeFromString()
{
// Values clearly in range
assertTrue(NumberInput.inLongRange("12345", false));
assertTrue(NumberInput.inLongRange("12345", true));
// Boundary testing
assertTrue(NumberInput.inLongRange("9223372036854775807", false));
assertFalse(NumberInput.inLongRange("9223372036854775808", false));
assertTrue(NumberInput.inLongRange("9223372036854775808", true));
assertFalse(NumberInput.inLongRange("9223372036854775809", true));
// Too many digits
assertFalse(NumberInput.inLongRange("12345678901234567890", false));
}
}