Skip to content

Commit

Permalink
Fix #30
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Oct 15, 2016
1 parent 9b00435 commit d8f82c7
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 33 deletions.
135 changes: 113 additions & 22 deletions cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
public final class CBORParser extends ParserMinimalBase
{
private final static byte[] NO_BYTES = new byte[0];

/**
* Enumeration that defines all togglable features for CBOR generators.
*/
Expand Down Expand Up @@ -66,13 +66,13 @@ private Feature(boolean defaultState) {
// Constants for handling of 16-bit "mini-floats"
private final static double MATH_POW_2_10 = Math.pow(2, 10);
private final static double MATH_POW_2_NEG14 = Math.pow(2, -14);

/*
/**********************************************************
/* Configuration
/**********************************************************
*/

/**
* Codec used for data binding when (if) requested.
*/
Expand Down Expand Up @@ -167,7 +167,7 @@ private Feature(boolean defaultState) {
* in the end it'll be converted to 1-based)
*/
protected int _tokenInputCol = 0;

/*
/**********************************************************
/* Parsing state
Expand All @@ -179,6 +179,7 @@ private Feature(boolean defaultState) {
* the next token is to be parsed (root, array, object).
*/
protected CBORReadContext _parsingContext;

/**
* Buffer that contains contents of String values, including
* field names if necessary (name split across boundary,
Expand All @@ -199,7 +200,7 @@ private Feature(boolean defaultState) {
* representation being available via read context)
*/
protected boolean _nameCopied = false;

/**
* ByteArrayBuilder is needed if 'getBinaryValue' is called. If so,
* we better reuse it for remainder of content.
Expand Down Expand Up @@ -247,7 +248,7 @@ private Feature(boolean defaultState) {
* buffer.
*/
protected boolean _bufferRecyclable;

/*
/**********************************************************
/* Additional parsing state
Expand Down Expand Up @@ -372,7 +373,7 @@ private Feature(boolean defaultState) {
/* Life-cycle
/**********************************************************
*/

public CBORParser(IOContext ctxt, int parserFeatures, int cborFeatures,
ObjectCodec codec, ByteQuadsCanonicalizer sym,
InputStream in, byte[] inputBuffer, int start, int end,
Expand Down Expand Up @@ -423,7 +424,7 @@ public Version version() {
/* Configuration
/**********************************************************
*/

// public JsonParser overrideStdFeatures(int values, int mask)

@Override
Expand Down Expand Up @@ -675,11 +676,30 @@ public JsonToken nextToken() throws IOException
_numberInt = _decode16Bits();
break;
case 2:
_numberInt = _decode32Bits();
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
{
int v = _decode32Bits();
if (v >= 0) {
_numberInt = v;
} else {
long l = (long) v;
_numberLong = l & 0xFFFFFFFFL;
_numTypesValid = NR_LONG;
}
}
break;
case 3:
_numberLong = _decode64Bits();
_numTypesValid = NR_LONG;
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
{
long l = _decode64Bits();
if (l >= 0L) {
_numberLong = l;
_numTypesValid = NR_LONG;
} else {
_numberBigInt = _bigPositive(l);
_numTypesValid = NR_BIGINT;
}
}
break;
default:
_invalidToken(ch);
Expand All @@ -699,11 +719,29 @@ public JsonToken nextToken() throws IOException
_numberInt = -_decode16Bits() - 1;
break;
case 2:
_numberInt = -_decode32Bits() - 1;
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
{
int v = _decode32Bits();
if (v < 0) {
_numberLong = ((long) v) + -1L;
_numTypesValid = NR_LONG;
} else {
_numberInt = -v - 1;
}
}
break;
case 3:
_numberLong = -_decode64Bits() - 1L;
_numTypesValid = NR_LONG;
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
{
long l = _decode64Bits();
if (l >= 0L) {
_numberLong = -l - 1L;
_numTypesValid = NR_LONG;
} else {
_numberBigInt = _bigNegative(l);
_numTypesValid = NR_BIGINT;
}
}
break;
default:
_invalidToken(ch);
Expand Down Expand Up @@ -784,7 +822,7 @@ public JsonToken nextToken() throws IOException
}
return null;
}

protected String _numberToName(int ch, boolean neg) throws IOException
{
final int lowBits = ch & 0x1F;
Expand Down Expand Up @@ -1109,11 +1147,30 @@ public String nextTextValue() throws IOException
_numberInt = _decode16Bits();
break;
case 2:
_numberInt = _decode32Bits();
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
{
int v = _decode32Bits();
if (v < 0) {
long l = (long) v;
_numberLong = l & 0xFFFFFFFFL;
_numTypesValid = NR_LONG;
} else{
_numberInt = v;
}
}
break;
case 3:
_numberLong = _decode64Bits();
_numTypesValid = NR_LONG;
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
{
long l = _decode64Bits();
if (l >= 0L) {
_numberLong = l;
_numTypesValid = NR_LONG;
} else {
_numberBigInt = _bigPositive(l);
_numTypesValid = NR_BIGINT;
}
}
break;
default:
_invalidToken(ch);
Expand All @@ -1134,11 +1191,29 @@ public String nextTextValue() throws IOException
_numberInt = -_decode16Bits() - 1;
break;
case 2:
_numberInt = -_decode32Bits() - 1;
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
{
int v = _decode32Bits();
if (v < 0) {
_numberLong = ((long) v) + -1L;
_numTypesValid = NR_LONG;
} else {
_numberInt = -v - 1;
}
}
break;
case 3:
_numberLong = -_decode64Bits() - 1L;
_numTypesValid = NR_LONG;
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
{
long l = _decode64Bits();
if (l >= 0L) {
_numberLong = l;
_numTypesValid = NR_LONG;
} else {
_numberBigInt = _bigNegative(l);
_numTypesValid = NR_BIGINT;
}
}
break;
default:
_invalidToken(ch);
Expand Down Expand Up @@ -3081,5 +3156,21 @@ protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException
_inputPtr = ptr;
_reportInvalidOther(mask);
}

/*
/**********************************************************
/* Internal methods, other
/**********************************************************
*/

private final static BigInteger BIT_63 = BigInteger.ONE.shiftLeft(63);

private final BigInteger _bigPositive(long l) {
BigInteger biggie = BigInteger.valueOf((l << 1) >>> 1);
return biggie.or(BIT_63);
}

private final BigInteger _bigNegative(long l) {
return BigInteger.valueOf(l).subtract(BigInteger.ONE);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
@SuppressWarnings("resource")
public class ParserNumbersTest extends CBORTestBase
{
private final CBORFactory CBOR_F = cborFactory();

public void testIntValues() throws Exception
{
// first, single-byte
Expand Down Expand Up @@ -71,13 +73,42 @@ private void _verifyInt(CBORFactory f, int value) throws Exception
p.close();
}

// Special tests for "gray area" for uint32 values that do not fit
// in Java int; from [dataformats-binary#30]
public void testInt32Overflow() throws Exception
{
// feed in max uint32, which is 2x+1 as big as Integer.MAX_VALUE
byte[] input = new byte[] {
(byte) CBORConstants.PREFIX_TYPE_INT_POS + 26, // uint32, that is, 4 more bytes
-1, -1, -1, -1
};
CBORParser p = CBOR_F.createParser(input);
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
// should be exposed as `long` because these uint32 values do not fit in Java `int`
assertEquals(0xFFFFFFFFL, p.getLongValue());
assertEquals(NumberType.LONG, p.getNumberType());
p.close();

// and then the reverse; something that ought to be negative
input = new byte[] {
(byte) CBORConstants.PREFIX_TYPE_INT_NEG + 26, // int32, that is, 4 more bytes
(byte) 0x80, 0, 0, 0
};
p = CBOR_F.createParser(input);
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
// should be exposed as `long` because this value won't fit in `int` either
long exp = -1L + Integer.MIN_VALUE;
assertEquals(exp, p.getLongValue());
assertEquals(NumberType.LONG, p.getNumberType());
p.close();
}

public void testLongValues() throws Exception
{
CBORFactory f = cborFactory();
_verifyLong(f, 1L + Integer.MAX_VALUE);
_verifyLong(f, Long.MIN_VALUE);
_verifyLong(f, Long.MAX_VALUE);
_verifyLong(f, -1L + Integer.MIN_VALUE);
_verifyLong(CBOR_F, 1L + Integer.MAX_VALUE);
_verifyLong(CBOR_F, Long.MIN_VALUE);
_verifyLong(CBOR_F, Long.MAX_VALUE);
_verifyLong(CBOR_F, -1L + Integer.MIN_VALUE);
}

private void _verifyLong(CBORFactory f, long value) throws Exception
Expand Down Expand Up @@ -107,14 +138,45 @@ private void _verifyLong(CBORFactory f, long value) throws Exception
p.close();
}


// Special tests for "gray area" for uint64 values that do not fit
// in Java long; from [dataformats-binary#30]
public void testInt64Overflow() throws Exception
{
// feed in max uint64, which is 2x+1 as big as Long.MAX_VALUE
byte[] input = new byte[] {
(byte) CBORConstants.PREFIX_TYPE_INT_POS + 27, // uint64, that is, 8 more bytes
-1, -1, -1, -1, -1, -1, -1, -1
};
CBORParser p = CBOR_F.createParser(input);
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
// should be exposed as BigInteger
assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
BigInteger exp = BigInteger.valueOf(Long.MAX_VALUE).shiftLeft(1)
.add(BigInteger.ONE);
assertEquals(exp, p.getBigIntegerValue());
p.close();

// and then the reverse; something that ought to be negative
input = new byte[] {
(byte) CBORConstants.PREFIX_TYPE_INT_NEG + 27,
(byte) 0x80, 0, 0, 0,
0, 0, 0, 0
};
p = CBOR_F.createParser(input);
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
// should be exposed as `long` because this value won't fit in `int` either
exp = BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE);
assertEquals(exp, p.getBigIntegerValue());
assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
p.close();
}

public void testDoubleValues() throws Exception
{
// first, single-byte
CBORFactory f = cborFactory();
// single byte
_verifyDouble(f, 0.25);
_verifyDouble(f, 20.5);
_verifyDouble(f, -5000.25);
_verifyDouble(CBOR_F, 0.25);
_verifyDouble(CBOR_F, 20.5);
_verifyDouble(CBOR_F, -5000.25);
}

private void _verifyDouble(CBORFactory f, double value) throws Exception
Expand Down
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Modules:

2.8.5 (not yet released)

#30 (cbor): Overflow when decoding uint32 for Major type 0
(reported by TianlinZhou@github)
#31 (cbor): Exception serializing double[][]

2.8.4 (14-Oct-2016)
Expand Down

0 comments on commit d8f82c7

Please sign in to comment.