Skip to content

Commit

Permalink
Fix #16
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed May 24, 2016
1 parent deaa07d commit cc5eb17
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 23 deletions.
1 change: 1 addition & 0 deletions cbor/release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Project: jackson-dataformat-cbor

2.8.0 (not yet released)

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,8 +513,31 @@ public final void writeEndObject() throws IOException {
}

@Override // since 2.8
public void writeArray(int[] array, int offset, int length)
throws IOException
public void writeArray(int[] array, int offset, int length) throws IOException
{
_verifyOffsets(array.length, offset, length);
// short-cut, do not create child array context etc
_verifyValueWrite("write int array");
_writeLengthMarker(PREFIX_TYPE_ARRAY, length);
for (int i = offset, end = offset+length; i < end; ++i) {
_writeNumberNoCheck(array[i]);
}
}

@Override // since 2.8
public void writeArray(long[] array, int offset, int length) throws IOException
{
_verifyOffsets(array.length, offset, length);
// short-cut, do not create child array context etc
_verifyValueWrite("write int array");
_writeLengthMarker(PREFIX_TYPE_ARRAY, length);
for (int i = offset, end = offset+length; i < end; ++i) {
_writeNumberNoCheck(array[i]);
}
}

@Override // since 2.8
public void writeArray(double[] array, int offset, int length) throws IOException
{
_verifyOffsets(array.length, offset, length);
// short-cut, do not create child array context etc
Expand Down Expand Up @@ -567,10 +590,59 @@ private final void _writeNumberNoCheck(int i) throws IOException {
_outputBuffer[_outputTail++] = b0;
}

private final void _writeNumberNoCheck(long l) throws IOException {
if (_cfgMinimalInts) {
if (l <= MAX_INT_AS_LONG && l >= MIN_INT_AS_LONG) {
_writeNumberNoCheck((int) l);
return;
}
}
_ensureRoomForOutput(9);
if (l < 0L) {
l += 1;
l = -l;
_outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + 27);
} else {
_outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + 27);
}
int i = (int) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
i = (int) l;
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
}

private final void _writeNumberNoCheck(double d) throws IOException {
_verifyValueWrite("write number");
_ensureRoomForOutput(11);
// 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems
// more accurate to use exact representation; and possibly faster.
// However, if there are cases where collapsing of NaN was needed (for
// non-Java clients), this can be changed
long l = Double.doubleToRawLongBits(d);
_outputBuffer[_outputTail++] = BYTE_FLOAT64;

int i = (int) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
i = (int) l;
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
}

/*
* /********************************************************** /* Output
* method implementations, textual
* /**********************************************************
/***********************************************************
/* Output method implementations, textual
/***********************************************************
*/

@Override
Expand Down Expand Up @@ -1094,11 +1166,10 @@ protected final void _ensureSpace(int needed) throws IOException {
}

protected final void _writeString(char[] text, int offset, int len)
throws IOException {
if (len <= MAX_SHORT_STRING_CHARS) { // possibly short strings (not
// necessarily)
_ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate
// length
throws IOException
{
if (len <= MAX_SHORT_STRING_CHARS) { // possibly short strings (not necessarily)
_ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate length
int actual = _encode(_outputTail + 1, text, offset, offset + len);
final byte[] buf = _outputBuffer;
int ix = _outputTail;
Expand All @@ -1115,8 +1186,7 @@ protected final void _writeString(char[] text, int offset, int len)
return;
}
if (len <= MAX_MEDIUM_STRING_CHARS) {
_ensureSpace(MAX_MEDIUM_STRING_BYTES); // short enough, can
// approximate
_ensureSpace(MAX_MEDIUM_STRING_BYTES); // short enough, can approximate
int actual = _encode(_outputTail + 2, text, offset, offset + len);
final byte[] buf = _outputBuffer;
int ix = _outputTail;
Expand All @@ -1139,7 +1209,7 @@ protected final void _writeString(char[] text, int offset, int len)
_ensureSpace(MAX_LONG_STRING_BYTES); // calculate accurate length to
// avoid extra flushing
int ix = _outputTail;
int actual = _encode(ix + 3, text, offset, offset + len);
int actual = _encode(ix + 3, text, offset, offset+len);
final byte[] buf = _outputBuffer;
buf[ix++] = BYTE_STRING_2BYTE_LEN;
buf[ix++] = (byte) (actual >> 8);
Expand All @@ -1151,16 +1221,21 @@ protected final void _writeString(char[] text, int offset, int len)
}

protected final void _writeChunkedString(char[] text, int offset, int len)
throws IOException {
throws IOException
{
// need to use a marker first
_writeByte(BYTE_STRING_INDEFINITE);

while (len > MAX_LONG_STRING_CHARS) {
_ensureSpace(MAX_LONG_STRING_BYTES); // marker and single-byte
// length?
_ensureSpace(MAX_LONG_STRING_BYTES); // marker and single-byte length?
int ix = _outputTail;
int actual = _encode(_outputTail + 3, text, offset, offset
+ MAX_LONG_STRING_CHARS);
// 23-May-2016, tatu: Make sure NOT to try to split surrogates in half
int end = offset + MAX_LONG_STRING_CHARS;
char c = text[end-1];
if (c >= SURR1_FIRST && c <= SURR1_LAST) {
--end;
}
int actual = _encode(_outputTail + 3, text, offset, end);
final byte[] buf = _outputBuffer;
buf[ix++] = BYTE_STRING_2BYTE_LEN;
buf[ix++] = (byte) (actual >> 8);
Expand Down Expand Up @@ -1223,8 +1298,7 @@ private final int _shortUTF8Encode2(char[] str, int i, int end,
}
// 3 or 4 bytes (surrogate)
// Surrogates?
if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte
// character
if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte character
outBuf[outputPtr++] = (byte) (0xe0 | (c >> 12));
outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package com.fasterxml.jackson.dataformat.cbor;

import java.io.ByteArrayOutputStream;

import com.fasterxml.jackson.core.*;

/**
* Basic testing for scalar-array write methods added in 2.8.
*/
public class ArrayGenerationTest extends CBORTestBase
{
private final CBORFactory FACTORY = new CBORFactory();

public void testIntArray() throws Exception
{
_testIntArray(false);
_testIntArray(true);
}

public void testLongArray() throws Exception
{
_testLongArray(false);
_testLongArray(true);
}

public void testDoubleArray() throws Exception
{
_testDoubleArray(false);
_testDoubleArray(true);
}

private void _testIntArray(boolean useBytes) throws Exception {
// first special cases of 0, 1 values
_testIntArray(0, 0, 0);
_testIntArray(0, 1, 1);

_testIntArray(1, 0, 0);
_testIntArray(1, 1, 1);

// and then some bigger data
_testIntArray(15, 0, 0);
_testIntArray(15, 2, 3);
_testIntArray(39, 0, 0);
_testIntArray(39, 4, 0);
_testIntArray(271, 0, 0);
_testIntArray(271, 0, 4);
_testIntArray(666, 0, 0);
_testIntArray(789, 0, 4);
_testIntArray(5009, 0, 0);
_testIntArray(7777, 0, 1);
}

private void _testLongArray(boolean useBytes) throws Exception {
// first special cases of 0, 1 values
_testLongArray(0, 0, 0);
_testLongArray(0, 1, 1);

_testLongArray(1, 0, 0);
_testLongArray(1, 1, 1);

// and then some bigger data
_testLongArray(15, 0, 0);
_testLongArray(15, 2, 3);
_testLongArray(39, 0, 0);
_testLongArray(39, 4, 0);
_testLongArray(271, 0, 0);
_testLongArray(271, 0, 4);
_testLongArray(911, 0, 0);
_testLongArray(1121, 0, 1);
_testLongArray(5009, 0, 0);
_testLongArray(6110, 0, 1);
}

private void _testDoubleArray(boolean useBytes) throws Exception {
// first special cases of 0, 1 values
_testDoubleArray(0, 0, 0);
_testDoubleArray(0, 1, 1);

_testDoubleArray(1, 0, 0);
_testDoubleArray(1, 1, 1);

// and then some bigger data
_testDoubleArray(15, 0, 0);
_testDoubleArray(15, 2, 3);
_testDoubleArray(39, 0, 0);
_testDoubleArray(39, 4, 0);
_testDoubleArray(271, 0, 0);
_testDoubleArray(271, 0, 4);
_testDoubleArray(744, 0, 0);
_testDoubleArray(999, 0, 4);
_testDoubleArray(5009, 0, 0);
_testDoubleArray(7256, 0, 1);
}

private void _testIntArray(int elements, int pre, int post) throws Exception
{
int[] values = new int[elements+pre+post];
for (int i = pre, end = pre+elements; i < end; ++i) {
values[i] = i-pre;
}
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
JsonGenerator gen = FACTORY.createGenerator(bytes);
gen.writeArray(values, pre, elements);
gen.close();

JsonParser p = FACTORY.createParser(bytes.toByteArray());
assertToken(JsonToken.START_ARRAY, p.nextToken());
for (int i = 0; i < elements; ++i) {
if ((i & 1) == 0) { // alternate
JsonToken t = p.nextToken();
if (t != JsonToken.VALUE_NUMBER_INT) {
fail("Expected number, got "+t+", element #"+i);
}
int act = p.getIntValue();
if (act != i) {
fail("Entry #"+i+", expected "+i+", got "+act);
}
} else {
assertEquals(i, p.nextIntValue(-1));
}
}
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
}

private void _testLongArray(int elements, int pre, int post) throws Exception
{
long[] values = new long[elements+pre+post];
for (int i = pre, end = pre+elements; i < end; ++i) {
values[i] = i-pre;
}
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
JsonGenerator gen = FACTORY.createGenerator(bytes);
gen.writeArray(values, pre, elements);
gen.close();
JsonParser p = FACTORY.createParser(bytes.toByteArray());
assertToken(JsonToken.START_ARRAY, p.nextToken());
for (int i = 0; i < elements; ++i) {
if ((i & 1) == 0) { // alternate
JsonToken t = p.nextToken();
if (t != JsonToken.VALUE_NUMBER_INT) {
fail("Expected number, got "+t+", element #"+i);
}
long act = p.getLongValue();
if (act != i) {
fail("Entry #"+i+", expected "+i+", got "+act);
}
} else {
assertEquals(i, p.nextLongValue(-1));
}
}
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
}

private void _testDoubleArray(int elements, int pre, int post) throws Exception
{
double[] values = new double[elements+pre+post];
for (int i = pre, end = pre+elements; i < end; ++i) {
values[i] = i-pre;
}
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
JsonGenerator gen = FACTORY.createGenerator(bytes);
gen.writeArray(values, pre, elements);
gen.close();
JsonParser p = FACTORY.createParser(bytes.toByteArray());
assertToken(JsonToken.START_ARRAY, p.nextToken());
for (int i = 0; i < elements; ++i) {
JsonToken t = p.nextToken();
if (t != JsonToken.VALUE_NUMBER_FLOAT) {
fail("Expected floating-point number, got "+t+", element #"+i);
}
assertEquals((double) i, p.getDoubleValue());
}
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
public abstract class CBORTestBase
extends junit.framework.TestCase
{

/*
/**********************************************************
/* Factory methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ private void _verifyStrings(JsonFactory f, byte[] input, List<String> strings)
assertToken(JsonToken.START_ARRAY, p.nextToken());
for (int i = 0, len = strings.size(); i < len; ++i) {
assertToken(JsonToken.VALUE_STRING, p.nextToken());
if ((i % 3) == 0) { // just for fun, try calling finish every now and then
p.finishToken();
}
assertEquals(strings.get(i), p.getText());
}
assertToken(JsonToken.END_ARRAY, p.nextToken());
Expand Down
Loading

0 comments on commit cc5eb17

Please sign in to comment.