Skip to content

Commit

Permalink
Fix #159
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed May 7, 2019
1 parent e405141 commit 4ac24ab
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,14 @@ public void writeBytes(byte[] data, int offset, int len) throws IOException {
/**********************************************************
*/

private final static int MAX_SHORT_STRING_CHARS = 23;
// in case it's > 23 bytes
private final static int MAX_SHORT_STRING_BYTES = 23 * 3 + 2;

private final static int MAX_MEDIUM_STRING_CHARS = 255;
// in case it's > 255 bytes
private final static int MAX_MEDIUM_STRING_BYTES = 255 * 3 + 3;

protected final void _writeString(String name) throws IOException {
int len = name.length();
if (len == 0) {
Expand All @@ -1203,7 +1211,7 @@ protected final void _writeString(String name) throws IOException {
int actual = _encode(_outputTail + 1, name, len);
final byte[] buf = _outputBuffer;
int ix = _outputTail;
if (actual < MAX_SHORT_STRING_CHARS) { // fits in prefix byte
if (actual <= MAX_SHORT_STRING_CHARS) { // fits in prefix byte
buf[ix++] = (byte) (PREFIX_TYPE_TEXT + actual);
_outputTail = ix + actual;
return;
Expand All @@ -1225,14 +1233,6 @@ protected final void _writeString(String name) throws IOException {
_writeString(cbuf, 0, len);
}

private final static int MAX_SHORT_STRING_CHARS = 23;
// in case it's > 23 bytes
private final static int MAX_SHORT_STRING_BYTES = 23 * 3 + 2;

private final static int MAX_MEDIUM_STRING_CHARS = 255;
// in case it's > 255 bytes
private final static int MAX_MEDIUM_STRING_BYTES = 255 * 3 + 3;

protected final void _ensureSpace(int needed) throws IOException {
if ((_outputTail + needed + 3) > _outputEnd) {
_flushBuffer();
Expand All @@ -1242,12 +1242,12 @@ 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)
if (len <= MAX_SHORT_STRING_CHARS) { // possibly short string (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;
if (actual < MAX_SHORT_STRING_CHARS) { // fits in prefix byte
if (actual <= MAX_SHORT_STRING_CHARS) { // fits in prefix byte
buf[ix++] = (byte) (PREFIX_TYPE_TEXT + actual);
_outputTail = ix + actual;
return;
Expand All @@ -1264,7 +1264,7 @@ protected final void _writeString(char[] text, int offset, int len)
int actual = _encode(_outputTail + 2, text, offset, offset + len);
final byte[] buf = _outputBuffer;
int ix = _outputTail;
if (actual < MAX_MEDIUM_STRING_CHARS) { // fits as expected
if (actual <= MAX_MEDIUM_STRING_CHARS) { // fits as expected
buf[ix++] = BYTE_STRING_1BYTE_LEN;
buf[ix++] = (byte) actual;
_outputTail = ix + actual;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,11 @@ protected static String generateUnicodeString(int length, Random rnd)
return sw.toString();
}

protected static String generateAsciiString(int length) {
return generateAsciiString(length, new Random(length));
protected static String generateLongAsciiString(int length) {
return generateLongAsciiString(length, new Random(length));
}

protected static String generateAsciiString(int length, Random rnd)
protected static String generateLongAsciiString(int length, Random rnd)
{
StringBuilder sw = new StringBuilder(length+10);
do {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.fasterxml.jackson.dataformat.cbor;

import java.io.ByteArrayOutputStream;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;

public class GeneratorShortStringTest extends CBORTestBase
{
public void testEmptyString() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (CBORGenerator gen = cborGenerator(out)) {
// First with String as input
gen.writeString("");
gen.close();
}
_verifyBytes(out.toByteArray(), CBORConstants.BYTE_EMPTY_STRING);

// then as char[]
out = new ByteArrayOutputStream();
try (CBORGenerator gen = cborGenerator(out)) {
gen.writeString(new char[0], 0, 0);
gen.close();
}
_verifyBytes(out.toByteArray(), CBORConstants.BYTE_EMPTY_STRING);
}

public void testShortTextAsString() throws Exception {
for (int len = 1; len <= 23; ++len) {
final String value = generateAsciiString(len);

ByteArrayOutputStream out = new ByteArrayOutputStream();
try (CBORGenerator gen = cborGenerator(out)) {
gen.writeString(value);
gen.close();
_verifyBytes(out.toByteArray(), (byte) (CBORConstants.PREFIX_TYPE_TEXT + len),
value.getBytes("UTF-8"));
_verifyString(out.toByteArray(), value);
}
}
}

public void testShortTextAsCharArray() throws Exception {
for (int len = 1; len <= 23; ++len) {
final String value = generateAsciiString(len);

ByteArrayOutputStream out = new ByteArrayOutputStream();
try (CBORGenerator gen = cborGenerator(out)) {
gen.writeString(value.toCharArray(), 0, len);
gen.close();
_verifyBytes(out.toByteArray(), (byte) (CBORConstants.PREFIX_TYPE_TEXT + len),
value.getBytes("UTF-8"));
_verifyString(out.toByteArray(), value);
}
}
}

public void testMediumTextAsString() throws Exception {
for (int len = 24; len <= 255; ++len) {
final String value = generateAsciiString(len);

ByteArrayOutputStream out = new ByteArrayOutputStream();
try (CBORGenerator gen = cborGenerator(out)) {
gen.writeString(value);
gen.close();
_verifyBytes(out.toByteArray(),
CBORConstants.BYTE_STRING_1BYTE_LEN, (byte) len,
value.getBytes("UTF-8"));
_verifyString(out.toByteArray(), value);
}
}
}

public void testMediumTextAsCharArray() throws Exception {
for (int len = 24; len <= 255; ++len) {
final String value = generateAsciiString(len);

ByteArrayOutputStream out = new ByteArrayOutputStream();
try (CBORGenerator gen = cborGenerator(out)) {
gen.writeString(value.toCharArray(), 0, len);
gen.close();
_verifyBytes(out.toByteArray(),
CBORConstants.BYTE_STRING_1BYTE_LEN, (byte) len,
value.getBytes("UTF-8"));
_verifyString(out.toByteArray(), value);
}
}
}

private String generateAsciiString(int len) {
StringBuilder sb = new StringBuilder(len);
while (--len >= 0) {
sb.append((char) ('0' + (len % 10)));
}
return sb.toString();
}

private void _verifyString(byte[] encoded, String value) throws Exception
{
try (JsonParser p = cborParser(encoded)) {
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(value, p.getText());
assertNull(p.nextToken());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -286,29 +286,13 @@ public void testTrivialObject() throws Exception
byte[] b = MAPPER.writeValueAsBytes(map);
_verifyBytes(b, EXP);
}

public void testShortText() throws Exception
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
CBORGenerator gen = cborGenerator(out);
gen.writeString("");
gen.close();
_verifyBytes(out.toByteArray(), CBORConstants.BYTE_EMPTY_STRING);

out = new ByteArrayOutputStream();
gen = cborGenerator(out);
gen.writeString("abc");
gen.close();
_verifyBytes(out.toByteArray(), (byte) (CBORConstants.PREFIX_TYPE_TEXT + 3),
(byte) 'a', (byte) 'b', (byte) 'c');
}

public void testLongerText() throws Exception
{
// First, something with 8-bit length
ByteArrayOutputStream out = new ByteArrayOutputStream();
CBORGenerator gen = cborGenerator(out);
final String SHORT_ASCII = generateAsciiString(240);
final String SHORT_ASCII = generateLongAsciiString(240);
gen.writeString(SHORT_ASCII);
gen.close();
byte[] b = SHORT_ASCII.getBytes("UTF-8");
Expand Down
4 changes: 4 additions & 0 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,7 @@ Michael Milkin (mmilkin@github)
Guido Medina (guidomedina@github)
* Reported #153: (smile) Unable to set a compression input/output decorator to a `SmileFactory`
(2.9.8)

Alexander Cyon (Sajjon@github)
* Reported #159: (cbor) Some short UTF Strings encoded using non-canonical form
(2.9.9)
5 changes: 5 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ Project: jackson-datatypes-binaryModules:
=== Releases ===
------------------------------------------------------------------------

2.9.9 (not yet released)

#159: (cbor) Some short UTF Strings encoded using non-canonical form
(reported by Alexander C)

2.9.8 (15-Dec-2018)

#140: (protobuf) Stack overflow when generating Protobuf schema on class with
Expand Down

0 comments on commit 4ac24ab

Please sign in to comment.