Skip to content

Commit b4db541

Browse files
committed
Fix #269
1 parent e697c72 commit b4db541

File tree

6 files changed

+222
-12
lines changed

6 files changed

+222
-12
lines changed

Diff for: cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -690,10 +690,13 @@ private final void _writeIntFull(int markerBase, int i) throws IOException
690690

691691
// Helper method that works like `writeNumber(long)` but DOES NOT
692692
// check internal output state. It does, however, check need for minimization
693-
private final void _writeLongNoCheck(long l) throws IOException {
693+
private final void _writeLongNoCheck(long l) throws IOException
694+
{
694695
if (_cfgMinimalInts) {
695696
if (l >= 0) {
696-
if (l <= 0x100000000L) {
697+
// 31-Mar-2021, tatu: [dataformats-cbor#269] Incorrect boundary check,
698+
// was off by one, resulting in truncation to 0
699+
if (l < 0x100000000L) {
697700
_writeIntMinimal(PREFIX_TYPE_INT_POS, (int) l);
698701
return;
699702
}
@@ -962,7 +965,9 @@ public void writeNumber(long l) throws IOException {
962965
_verifyValueWrite("write number");
963966
if (_cfgMinimalInts) { // maybe 32 bits is enough?
964967
if (l >= 0) {
965-
if (l <= 0x100000000L) {
968+
// 31-Mar-2021, tatu: [dataformats-cbor#269] Incorrect boundary check,
969+
// was off by one, resulting in truncation to 0
970+
if (l < 0x100000000L) {
966971
_writeIntMinimal(PREFIX_TYPE_INT_POS, (int) l);
967972
return;
968973
}

Diff for: cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java

+13
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,19 @@ protected String _numberToName(int ch, boolean neg) throws IOException
817817
break;
818818
case 26:
819819
i = _decode32Bits();
820+
// [dataformats-binary#269] (and earlier [dataformats-binary#30]),
821+
// got some edge case to consider
822+
if (i < 0) {
823+
long l;
824+
if (neg) {
825+
long unsignedBase = (long) i & 0xFFFFFFFFL;
826+
l = -unsignedBase - 1L;
827+
} else {
828+
l = (long) i;
829+
l = l & 0xFFFFFFFFL;
830+
}
831+
return String.valueOf(l);
832+
}
820833
break;
821834
case 27:
822835
{

Diff for: cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/mapper/NumberBeanTest.java

+129-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
package com.fasterxml.jackson.dataformat.cbor.mapper;
22

3+
import java.io.ByteArrayOutputStream;
4+
import java.math.BigDecimal;
5+
import java.math.BigInteger;
6+
7+
import com.fasterxml.jackson.annotation.JsonUnwrapped;
8+
39
import com.fasterxml.jackson.databind.ObjectMapper;
10+
11+
import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;
412
import com.fasterxml.jackson.dataformat.cbor.CBORTestBase;
513

614
public class NumberBeanTest extends CBORTestBase
@@ -19,6 +27,23 @@ static class LongBean {
1927
protected LongBean() { }
2028
}
2129

30+
static class NumberWrapper {
31+
public Number nr;
32+
}
33+
34+
// // Copied form "JDKNumberDeserTest", related to BigDecimal precision
35+
// // retaining through buffering
36+
37+
// [databind#2784]
38+
static class BigDecimalHolder2784 {
39+
public BigDecimal value;
40+
}
41+
42+
static class NestedBigDecimalHolder2784 {
43+
@JsonUnwrapped
44+
public BigDecimalHolder2784 holder;
45+
}
46+
2247
/*
2348
/**********************************************************
2449
/* Test methods
@@ -46,15 +71,28 @@ public void testLongRoundTrip() throws Exception
4671
100L, -200L,
4772
5000L, -3600L,
4873
Integer.MIN_VALUE, Integer.MAX_VALUE,
49-
1L + Integer.MAX_VALUE, -1L + Integer.MIN_VALUE,
50-
2330462449L, // from [dataformats-binary#30]
51-
Long.MIN_VALUE, Long.MAX_VALUE
52-
}) {
53-
LongBean input = new LongBean(v);
54-
byte[] b = MAPPER.writeValueAsBytes(input);
55-
LongBean result = MAPPER.readValue(b, LongBean.class);
56-
assertEquals(input.value, result.value);
74+
1L + Integer.MAX_VALUE, -1L + Integer.MIN_VALUE
75+
}) {
76+
_testLongRoundTrip(v);
5777
}
78+
79+
_testLongRoundTrip(2330462449L); // from [dataformats-binary#30]
80+
_testLongRoundTrip(0xFFFFFFFFL); // max positive uint32
81+
_testLongRoundTrip(-0xFFFFFFFFL);
82+
_testLongRoundTrip(0x100000000L);
83+
_testLongRoundTrip(-0x100000000L);
84+
_testLongRoundTrip(0x100000001L);
85+
_testLongRoundTrip(-0x100000001L);
86+
_testLongRoundTrip(Long.MIN_VALUE);
87+
_testLongRoundTrip(Long.MAX_VALUE);
88+
}
89+
90+
private void _testLongRoundTrip(long v) throws Exception
91+
{
92+
LongBean input = new LongBean(v);
93+
byte[] b = MAPPER.writeValueAsBytes(input);
94+
LongBean result = MAPPER.readValue(b, LongBean.class);
95+
assertEquals(input.value, result.value);
5896
}
5997

6098
// for [dataformats-binary#32] coercion of Float into Double
@@ -67,4 +105,87 @@ public void testUntypedWithFloat() throws Exception
67105
assertEquals(Float.class, result[0].getClass());
68106
assertEquals(input[0], result[0]);
69107
}
108+
109+
public void testNumberTypeRetainingInt() throws Exception
110+
{
111+
NumberWrapper result;
112+
ByteArrayOutputStream bytes;
113+
114+
bytes = new ByteArrayOutputStream();
115+
try (CBORGenerator g = cborGenerator(bytes)) {
116+
g.writeStartObject();
117+
g.writeNumberField("nr", 123);
118+
g.writeEndObject();
119+
}
120+
result = MAPPER.readValue(bytes.toByteArray(), NumberWrapper.class);
121+
assertEquals(Integer.valueOf(123), result.nr);
122+
123+
bytes = new ByteArrayOutputStream();
124+
try (CBORGenerator g = cborGenerator(bytes)) {
125+
g.writeStartObject();
126+
g.writeNumberField("nr", Long.MAX_VALUE);
127+
g.writeEndObject();
128+
}
129+
result = MAPPER.readValue(bytes.toByteArray(), NumberWrapper.class);
130+
assertEquals(Long.valueOf(Long.MAX_VALUE), result.nr);
131+
132+
bytes = new ByteArrayOutputStream();
133+
try (CBORGenerator g = cborGenerator(bytes)) {
134+
g.writeStartObject();
135+
g.writeNumberField("nr", BigInteger.valueOf(-42L));
136+
g.writeEndObject();
137+
}
138+
result = MAPPER.readValue(bytes.toByteArray(), NumberWrapper.class);
139+
assertEquals(BigInteger.valueOf(-42L), result.nr);
140+
}
141+
142+
public void testNumberTypeRetainingFP() throws Exception
143+
{
144+
NumberWrapper result;
145+
ByteArrayOutputStream bytes;
146+
147+
bytes = new ByteArrayOutputStream();
148+
try (CBORGenerator g = cborGenerator(bytes)) {
149+
g.writeStartObject();
150+
g.writeNumberField("nr", 0.25f);
151+
g.writeEndObject();
152+
}
153+
result = MAPPER.readValue(bytes.toByteArray(), NumberWrapper.class);
154+
assertEquals(Float.valueOf(0.25f), result.nr);
155+
156+
bytes = new ByteArrayOutputStream();
157+
try (CBORGenerator g = cborGenerator(bytes)) {
158+
g.writeStartObject();
159+
g.writeNumberField("nr", 0.5);
160+
g.writeEndObject();
161+
}
162+
result = MAPPER.readValue(bytes.toByteArray(), NumberWrapper.class);
163+
assertEquals(Double.valueOf(0.5), result.nr);
164+
165+
bytes = new ByteArrayOutputStream();
166+
try (CBORGenerator g = cborGenerator(bytes)) {
167+
g.writeStartObject();
168+
g.writeNumberField("nr", new BigDecimal("0.100"));
169+
g.writeEndObject();
170+
}
171+
result = MAPPER.readValue(bytes.toByteArray(), NumberWrapper.class);
172+
assertEquals(new BigDecimal("0.100"), result.nr);
173+
}
174+
175+
// [databind#2784]
176+
public void testBigDecimalWithBuffering() throws Exception
177+
{
178+
final BigDecimal VALUE = new BigDecimal("5.00");
179+
// Need to generate by hand since JSON would not indicate desire for BigDecimal
180+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
181+
try (CBORGenerator g = cborGenerator(bytes)) {
182+
g.writeStartObject();
183+
g.writeNumberField("value", VALUE);
184+
g.writeEndObject();
185+
}
186+
187+
NestedBigDecimalHolder2784 result = MAPPER.readValue(bytes.toByteArray(),
188+
NestedBigDecimalHolder2784.class);
189+
assertEquals(VALUE, result.holder.value);
190+
}
70191
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.fasterxml.jackson.dataformat.cbor.mapper;
2+
3+
import java.util.*;
4+
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.fasterxml.jackson.dataformat.cbor.CBORTestBase;
7+
8+
// [dataformats-binary#269]
9+
public class NumberMap269Test extends CBORTestBase
10+
{
11+
static class TestData269 {
12+
Map<Long, String> map;
13+
14+
public TestData269 setMap(Map<Long, String> map) {
15+
this.map = map;
16+
return this;
17+
}
18+
19+
public Map<Long, String> getMap() {
20+
return map;
21+
}
22+
}
23+
24+
/*
25+
/**********************************************************
26+
/* Test methods
27+
/**********************************************************
28+
*/
29+
30+
private final ObjectMapper MAPPER = cborMapper();
31+
32+
// [dataformats-binary#269]
33+
public void testInt32BoundaryWithMapKey() throws Exception
34+
{
35+
// First, with specific reported combo:
36+
_testInt32BoundaryWithMapKey(4294967296L, -4294967296L);
37+
38+
// and then systematically couple of others (actually overlapping but...)
39+
final long MAX_POS_UINT32 = 0xFFFFFFFFL;
40+
final long MAX_POS_UINT32_PLUS_1 = MAX_POS_UINT32 + 1L;
41+
42+
_testInt32BoundaryWithMapKey(MAX_POS_UINT32, -MAX_POS_UINT32);
43+
_testInt32BoundaryWithMapKey(MAX_POS_UINT32_PLUS_1,
44+
-MAX_POS_UINT32_PLUS_1);
45+
46+
_testInt32BoundaryWithMapKey(MAX_POS_UINT32_PLUS_1 + 1L,
47+
-MAX_POS_UINT32_PLUS_1 - 1L);
48+
}
49+
50+
private void _testInt32BoundaryWithMapKey(long key1, long key2) throws Exception
51+
{
52+
Map<Long, String> map = new LinkedHashMap<>();
53+
map.put(key1, "hello");
54+
map.put(key2, "world");
55+
TestData269 input = new TestData269().setMap(map);
56+
57+
byte[] cborDoc = MAPPER.writeValueAsBytes(input);
58+
59+
TestData269 result = MAPPER.readValue(cborDoc, TestData269.class);
60+
61+
assertEquals(input.map, result.map);
62+
}
63+
}

Diff for: release-notes/CREDITS-2.x

+4-1
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,7 @@ Jonas Konrad (yawkat@github)
140140
* Reported, contributed fix for #201: `CBORGenerator.Feature.WRITE_MINIMAL_INTS` does not write
141141
most compact form for all integers
142142
(2.11.0)
143-
143+
144+
Dylan (Quantum64@github)
145+
* Reported #269: CBOR loses `Map` entries with specific `long` Map key values (32-bit boundary)
146+
(2.11.5 / 2.12.0)

Diff for: release-notes/VERSION-2.x

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ Modules:
99
=== Releases ===
1010
------------------------------------------------------------------------
1111

12+
Not yet releasaed:
13+
14+
#269: CBOR loses `Map` entries with specific `long` Map key values (32-bit boundary)
15+
(reported by Quantum64@github)
16+
1217
2.11.4 (12-Dec-2020)
1318

1419
#186: (cbor) Eager allocation of byte buffer can cause `java.lang.OutOfMemoryError`

0 commit comments

Comments
 (0)