Skip to content

Commit cc5eb17

Browse files
committed
Fix #16
1 parent deaa07d commit cc5eb17

File tree

7 files changed

+316
-23
lines changed

7 files changed

+316
-23
lines changed

cbor/release-notes/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Project: jackson-dataformat-cbor
66

77
2.8.0 (not yet released)
88

9+
#16: Implement `JsonGenerator.writeArray()` methods added in `jackson-core` (2.8)
910
#17: Support parsing of `BigInteger`, `BigDecimal`, not just generating
1011
#18: Fail to report error for trying to write field name outside Object (root level)
1112

cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java

Lines changed: 94 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -513,8 +513,31 @@ public final void writeEndObject() throws IOException {
513513
}
514514

515515
@Override // since 2.8
516-
public void writeArray(int[] array, int offset, int length)
517-
throws IOException
516+
public void writeArray(int[] array, int offset, int length) throws IOException
517+
{
518+
_verifyOffsets(array.length, offset, length);
519+
// short-cut, do not create child array context etc
520+
_verifyValueWrite("write int array");
521+
_writeLengthMarker(PREFIX_TYPE_ARRAY, length);
522+
for (int i = offset, end = offset+length; i < end; ++i) {
523+
_writeNumberNoCheck(array[i]);
524+
}
525+
}
526+
527+
@Override // since 2.8
528+
public void writeArray(long[] array, int offset, int length) throws IOException
529+
{
530+
_verifyOffsets(array.length, offset, length);
531+
// short-cut, do not create child array context etc
532+
_verifyValueWrite("write int array");
533+
_writeLengthMarker(PREFIX_TYPE_ARRAY, length);
534+
for (int i = offset, end = offset+length; i < end; ++i) {
535+
_writeNumberNoCheck(array[i]);
536+
}
537+
}
538+
539+
@Override // since 2.8
540+
public void writeArray(double[] array, int offset, int length) throws IOException
518541
{
519542
_verifyOffsets(array.length, offset, length);
520543
// short-cut, do not create child array context etc
@@ -567,10 +590,59 @@ private final void _writeNumberNoCheck(int i) throws IOException {
567590
_outputBuffer[_outputTail++] = b0;
568591
}
569592

593+
private final void _writeNumberNoCheck(long l) throws IOException {
594+
if (_cfgMinimalInts) {
595+
if (l <= MAX_INT_AS_LONG && l >= MIN_INT_AS_LONG) {
596+
_writeNumberNoCheck((int) l);
597+
return;
598+
}
599+
}
600+
_ensureRoomForOutput(9);
601+
if (l < 0L) {
602+
l += 1;
603+
l = -l;
604+
_outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + 27);
605+
} else {
606+
_outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + 27);
607+
}
608+
int i = (int) (l >> 32);
609+
_outputBuffer[_outputTail++] = (byte) (i >> 24);
610+
_outputBuffer[_outputTail++] = (byte) (i >> 16);
611+
_outputBuffer[_outputTail++] = (byte) (i >> 8);
612+
_outputBuffer[_outputTail++] = (byte) i;
613+
i = (int) l;
614+
_outputBuffer[_outputTail++] = (byte) (i >> 24);
615+
_outputBuffer[_outputTail++] = (byte) (i >> 16);
616+
_outputBuffer[_outputTail++] = (byte) (i >> 8);
617+
_outputBuffer[_outputTail++] = (byte) i;
618+
}
619+
620+
private final void _writeNumberNoCheck(double d) throws IOException {
621+
_verifyValueWrite("write number");
622+
_ensureRoomForOutput(11);
623+
// 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems
624+
// more accurate to use exact representation; and possibly faster.
625+
// However, if there are cases where collapsing of NaN was needed (for
626+
// non-Java clients), this can be changed
627+
long l = Double.doubleToRawLongBits(d);
628+
_outputBuffer[_outputTail++] = BYTE_FLOAT64;
629+
630+
int i = (int) (l >> 32);
631+
_outputBuffer[_outputTail++] = (byte) (i >> 24);
632+
_outputBuffer[_outputTail++] = (byte) (i >> 16);
633+
_outputBuffer[_outputTail++] = (byte) (i >> 8);
634+
_outputBuffer[_outputTail++] = (byte) i;
635+
i = (int) l;
636+
_outputBuffer[_outputTail++] = (byte) (i >> 24);
637+
_outputBuffer[_outputTail++] = (byte) (i >> 16);
638+
_outputBuffer[_outputTail++] = (byte) (i >> 8);
639+
_outputBuffer[_outputTail++] = (byte) i;
640+
}
641+
570642
/*
571-
* /********************************************************** /* Output
572-
* method implementations, textual
573-
* /**********************************************************
643+
/***********************************************************
644+
/* Output method implementations, textual
645+
/***********************************************************
574646
*/
575647

576648
@Override
@@ -1094,11 +1166,10 @@ protected final void _ensureSpace(int needed) throws IOException {
10941166
}
10951167

10961168
protected final void _writeString(char[] text, int offset, int len)
1097-
throws IOException {
1098-
if (len <= MAX_SHORT_STRING_CHARS) { // possibly short strings (not
1099-
// necessarily)
1100-
_ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate
1101-
// length
1169+
throws IOException
1170+
{
1171+
if (len <= MAX_SHORT_STRING_CHARS) { // possibly short strings (not necessarily)
1172+
_ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate length
11021173
int actual = _encode(_outputTail + 1, text, offset, offset + len);
11031174
final byte[] buf = _outputBuffer;
11041175
int ix = _outputTail;
@@ -1115,8 +1186,7 @@ protected final void _writeString(char[] text, int offset, int len)
11151186
return;
11161187
}
11171188
if (len <= MAX_MEDIUM_STRING_CHARS) {
1118-
_ensureSpace(MAX_MEDIUM_STRING_BYTES); // short enough, can
1119-
// approximate
1189+
_ensureSpace(MAX_MEDIUM_STRING_BYTES); // short enough, can approximate
11201190
int actual = _encode(_outputTail + 2, text, offset, offset + len);
11211191
final byte[] buf = _outputBuffer;
11221192
int ix = _outputTail;
@@ -1139,7 +1209,7 @@ protected final void _writeString(char[] text, int offset, int len)
11391209
_ensureSpace(MAX_LONG_STRING_BYTES); // calculate accurate length to
11401210
// avoid extra flushing
11411211
int ix = _outputTail;
1142-
int actual = _encode(ix + 3, text, offset, offset + len);
1212+
int actual = _encode(ix + 3, text, offset, offset+len);
11431213
final byte[] buf = _outputBuffer;
11441214
buf[ix++] = BYTE_STRING_2BYTE_LEN;
11451215
buf[ix++] = (byte) (actual >> 8);
@@ -1151,16 +1221,21 @@ protected final void _writeString(char[] text, int offset, int len)
11511221
}
11521222

11531223
protected final void _writeChunkedString(char[] text, int offset, int len)
1154-
throws IOException {
1224+
throws IOException
1225+
{
11551226
// need to use a marker first
11561227
_writeByte(BYTE_STRING_INDEFINITE);
11571228

11581229
while (len > MAX_LONG_STRING_CHARS) {
1159-
_ensureSpace(MAX_LONG_STRING_BYTES); // marker and single-byte
1160-
// length?
1230+
_ensureSpace(MAX_LONG_STRING_BYTES); // marker and single-byte length?
11611231
int ix = _outputTail;
1162-
int actual = _encode(_outputTail + 3, text, offset, offset
1163-
+ MAX_LONG_STRING_CHARS);
1232+
// 23-May-2016, tatu: Make sure NOT to try to split surrogates in half
1233+
int end = offset + MAX_LONG_STRING_CHARS;
1234+
char c = text[end-1];
1235+
if (c >= SURR1_FIRST && c <= SURR1_LAST) {
1236+
--end;
1237+
}
1238+
int actual = _encode(_outputTail + 3, text, offset, end);
11641239
final byte[] buf = _outputBuffer;
11651240
buf[ix++] = BYTE_STRING_2BYTE_LEN;
11661241
buf[ix++] = (byte) (actual >> 8);
@@ -1223,8 +1298,7 @@ private final int _shortUTF8Encode2(char[] str, int i, int end,
12231298
}
12241299
// 3 or 4 bytes (surrogate)
12251300
// Surrogates?
1226-
if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte
1227-
// character
1301+
if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte character
12281302
outBuf[outputPtr++] = (byte) (0xe0 | (c >> 12));
12291303
outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
12301304
outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f));
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package com.fasterxml.jackson.dataformat.cbor;
2+
3+
import java.io.ByteArrayOutputStream;
4+
5+
import com.fasterxml.jackson.core.*;
6+
7+
/**
8+
* Basic testing for scalar-array write methods added in 2.8.
9+
*/
10+
public class ArrayGenerationTest extends CBORTestBase
11+
{
12+
private final CBORFactory FACTORY = new CBORFactory();
13+
14+
public void testIntArray() throws Exception
15+
{
16+
_testIntArray(false);
17+
_testIntArray(true);
18+
}
19+
20+
public void testLongArray() throws Exception
21+
{
22+
_testLongArray(false);
23+
_testLongArray(true);
24+
}
25+
26+
public void testDoubleArray() throws Exception
27+
{
28+
_testDoubleArray(false);
29+
_testDoubleArray(true);
30+
}
31+
32+
private void _testIntArray(boolean useBytes) throws Exception {
33+
// first special cases of 0, 1 values
34+
_testIntArray(0, 0, 0);
35+
_testIntArray(0, 1, 1);
36+
37+
_testIntArray(1, 0, 0);
38+
_testIntArray(1, 1, 1);
39+
40+
// and then some bigger data
41+
_testIntArray(15, 0, 0);
42+
_testIntArray(15, 2, 3);
43+
_testIntArray(39, 0, 0);
44+
_testIntArray(39, 4, 0);
45+
_testIntArray(271, 0, 0);
46+
_testIntArray(271, 0, 4);
47+
_testIntArray(666, 0, 0);
48+
_testIntArray(789, 0, 4);
49+
_testIntArray(5009, 0, 0);
50+
_testIntArray(7777, 0, 1);
51+
}
52+
53+
private void _testLongArray(boolean useBytes) throws Exception {
54+
// first special cases of 0, 1 values
55+
_testLongArray(0, 0, 0);
56+
_testLongArray(0, 1, 1);
57+
58+
_testLongArray(1, 0, 0);
59+
_testLongArray(1, 1, 1);
60+
61+
// and then some bigger data
62+
_testLongArray(15, 0, 0);
63+
_testLongArray(15, 2, 3);
64+
_testLongArray(39, 0, 0);
65+
_testLongArray(39, 4, 0);
66+
_testLongArray(271, 0, 0);
67+
_testLongArray(271, 0, 4);
68+
_testLongArray(911, 0, 0);
69+
_testLongArray(1121, 0, 1);
70+
_testLongArray(5009, 0, 0);
71+
_testLongArray(6110, 0, 1);
72+
}
73+
74+
private void _testDoubleArray(boolean useBytes) throws Exception {
75+
// first special cases of 0, 1 values
76+
_testDoubleArray(0, 0, 0);
77+
_testDoubleArray(0, 1, 1);
78+
79+
_testDoubleArray(1, 0, 0);
80+
_testDoubleArray(1, 1, 1);
81+
82+
// and then some bigger data
83+
_testDoubleArray(15, 0, 0);
84+
_testDoubleArray(15, 2, 3);
85+
_testDoubleArray(39, 0, 0);
86+
_testDoubleArray(39, 4, 0);
87+
_testDoubleArray(271, 0, 0);
88+
_testDoubleArray(271, 0, 4);
89+
_testDoubleArray(744, 0, 0);
90+
_testDoubleArray(999, 0, 4);
91+
_testDoubleArray(5009, 0, 0);
92+
_testDoubleArray(7256, 0, 1);
93+
}
94+
95+
private void _testIntArray(int elements, int pre, int post) throws Exception
96+
{
97+
int[] values = new int[elements+pre+post];
98+
for (int i = pre, end = pre+elements; i < end; ++i) {
99+
values[i] = i-pre;
100+
}
101+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
102+
JsonGenerator gen = FACTORY.createGenerator(bytes);
103+
gen.writeArray(values, pre, elements);
104+
gen.close();
105+
106+
JsonParser p = FACTORY.createParser(bytes.toByteArray());
107+
assertToken(JsonToken.START_ARRAY, p.nextToken());
108+
for (int i = 0; i < elements; ++i) {
109+
if ((i & 1) == 0) { // alternate
110+
JsonToken t = p.nextToken();
111+
if (t != JsonToken.VALUE_NUMBER_INT) {
112+
fail("Expected number, got "+t+", element #"+i);
113+
}
114+
int act = p.getIntValue();
115+
if (act != i) {
116+
fail("Entry #"+i+", expected "+i+", got "+act);
117+
}
118+
} else {
119+
assertEquals(i, p.nextIntValue(-1));
120+
}
121+
}
122+
assertToken(JsonToken.END_ARRAY, p.nextToken());
123+
p.close();
124+
}
125+
126+
private void _testLongArray(int elements, int pre, int post) throws Exception
127+
{
128+
long[] values = new long[elements+pre+post];
129+
for (int i = pre, end = pre+elements; i < end; ++i) {
130+
values[i] = i-pre;
131+
}
132+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
133+
JsonGenerator gen = FACTORY.createGenerator(bytes);
134+
gen.writeArray(values, pre, elements);
135+
gen.close();
136+
JsonParser p = FACTORY.createParser(bytes.toByteArray());
137+
assertToken(JsonToken.START_ARRAY, p.nextToken());
138+
for (int i = 0; i < elements; ++i) {
139+
if ((i & 1) == 0) { // alternate
140+
JsonToken t = p.nextToken();
141+
if (t != JsonToken.VALUE_NUMBER_INT) {
142+
fail("Expected number, got "+t+", element #"+i);
143+
}
144+
long act = p.getLongValue();
145+
if (act != i) {
146+
fail("Entry #"+i+", expected "+i+", got "+act);
147+
}
148+
} else {
149+
assertEquals(i, p.nextLongValue(-1));
150+
}
151+
}
152+
assertToken(JsonToken.END_ARRAY, p.nextToken());
153+
p.close();
154+
}
155+
156+
private void _testDoubleArray(int elements, int pre, int post) throws Exception
157+
{
158+
double[] values = new double[elements+pre+post];
159+
for (int i = pre, end = pre+elements; i < end; ++i) {
160+
values[i] = i-pre;
161+
}
162+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
163+
JsonGenerator gen = FACTORY.createGenerator(bytes);
164+
gen.writeArray(values, pre, elements);
165+
gen.close();
166+
JsonParser p = FACTORY.createParser(bytes.toByteArray());
167+
assertToken(JsonToken.START_ARRAY, p.nextToken());
168+
for (int i = 0; i < elements; ++i) {
169+
JsonToken t = p.nextToken();
170+
if (t != JsonToken.VALUE_NUMBER_FLOAT) {
171+
fail("Expected floating-point number, got "+t+", element #"+i);
172+
}
173+
assertEquals((double) i, p.getDoubleValue());
174+
}
175+
assertToken(JsonToken.END_ARRAY, p.nextToken());
176+
p.close();
177+
}
178+
}

cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/CBORTestBase.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
public abstract class CBORTestBase
1515
extends junit.framework.TestCase
1616
{
17-
1817
/*
1918
/**********************************************************
2019
/* Factory methods

cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/GeneratorLongStringTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ private void _verifyStrings(JsonFactory f, byte[] input, List<String> strings)
7171
assertToken(JsonToken.START_ARRAY, p.nextToken());
7272
for (int i = 0, len = strings.size(); i < len; ++i) {
7373
assertToken(JsonToken.VALUE_STRING, p.nextToken());
74+
if ((i % 3) == 0) { // just for fun, try calling finish every now and then
75+
p.finishToken();
76+
}
7477
assertEquals(strings.get(i), p.getText());
7578
}
7679
assertToken(JsonToken.END_ARRAY, p.nextToken());

0 commit comments

Comments
 (0)