diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java index 33e489dd84..a8acfc8741 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java @@ -420,38 +420,17 @@ public void writeString(String text) throws IOException _writeNull(); return; } - // First: can we make a local copy of chars that make up text? + // First: if we can't guarantee it all fits, quoted, within output, offline final int len = text.length(); - if (len > _charBufferLength) { // nope: off-line handling + if (len > _outputMaxContiguous) { // nope: off-line handling _writeStringSegments(text, true); return; } - // Output: if we can't guarantee it fits in output buffer, off-line as well: - if (len > _outputMaxContiguous) { - _writeLongString(_charBuffer, 0, len); - return; - } if ((_outputTail + len) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; _writeStringSegment(text, 0, len); // we checked space already above - /* [JACKSON-462] But that method may have had to expand multi-byte Unicode - * chars, so we must check again - */ - if (_outputTail >= _outputEnd) { - _flushBuffer(); - } - _outputBuffer[_outputTail++] = BYTE_QUOTE; - } - - private void _writeLongString(char[] text, int offset, int len) throws IOException - { - if (_outputTail >= _outputEnd) { - _flushBuffer(); - } - _outputBuffer[_outputTail++] = BYTE_QUOTE; - _writeStringSegments(text, 0, len); if (_outputTail >= _outputEnd) { _flushBuffer(); } @@ -1155,15 +1134,13 @@ private final void _writeStringSegments(String text, boolean addQuotes) throws I int left = text.length(); int offset = 0; - final char[] cbuf = _charBuffer; while (left > 0) { int len = Math.min(_outputMaxContiguous, left); - text.getChars(offset, offset+len, cbuf, 0); if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space _flushBuffer(); } - _writeStringSegment(cbuf, 0, len); + _writeStringSegment(text, offset, len); offset += len; left -= len; } diff --git a/src/test/java/com/fasterxml/jackson/core/main/TestStringGeneration.java b/src/test/java/com/fasterxml/jackson/core/json/StringGenerationTest.java similarity index 76% rename from src/test/java/com/fasterxml/jackson/core/main/TestStringGeneration.java rename to src/test/java/com/fasterxml/jackson/core/json/StringGenerationTest.java index ed167e6dfb..b36cbe6860 100644 --- a/src/test/java/com/fasterxml/jackson/core/main/TestStringGeneration.java +++ b/src/test/java/com/fasterxml/jackson/core/json/StringGenerationTest.java @@ -1,4 +1,4 @@ -package com.fasterxml.jackson.core.main; +package com.fasterxml.jackson.core.json; import java.io.*; @@ -10,7 +10,7 @@ * Set of basic unit tests for verifying that the string * generation, including character escaping, works as expected. */ -public class TestStringGeneration +public class StringGenerationTest extends BaseTest { final static String[] SAMPLES = new String[] { @@ -23,15 +23,33 @@ public class TestStringGeneration private final JsonFactory FACTORY = new JsonFactory(); - public void testBasicEscaping() - throws Exception + public void testBasicEscaping() throws Exception { doTestBasicEscaping(false); doTestBasicEscaping(true); } - public void testLongerRandomSingleChunk() - throws Exception + // for [core#194] + public void testMediumStringsBytes() throws Exception + { + _testMediumStrings(true, 1100); + _testMediumStrings(true, 2300); + _testMediumStrings(true, 3800); + _testMediumStrings(true, 7500); + _testMediumStrings(true, 19000); + } + + // for [core#194] + public void testMediumStringsChars() throws Exception + { + _testMediumStrings(false, 1100); + _testMediumStrings(false, 2300); + _testMediumStrings(false, 3800); + _testMediumStrings(false, 7500); + _testMediumStrings(false, 19000); + } + + public void testLongerRandomSingleChunk() throws Exception { /* Let's first generate 100k of pseudo-random characters, favoring * 7-bit ascii range @@ -43,8 +61,7 @@ public void testLongerRandomSingleChunk() } } - public void testLongerRandomMultiChunk() - throws Exception + public void testLongerRandomMultiChunk() throws Exception { /* Let's first generate 100k of pseudo-random characters, favoring * 7-bit ascii range @@ -62,6 +79,29 @@ public void testLongerRandomMultiChunk() /********************************************************** */ + private String _generareMediumText(int minLen) + { + StringBuilder sb = new StringBuilder(minLen + 1000); + Random rnd = new Random(minLen); + do { + switch (rnd.nextInt() % 4) { + case 0: + sb.append(" foo"); + break; + case 1: + sb.append(" bar"); + break; + case 2: + sb.append(String.valueOf(sb.length())); + break; + default: + sb.append(" \"stuff\""); + break; + } + } while (sb.length() < minLen); + return sb.toString(); + } + private String generateRandom(int len) { StringBuilder sb = new StringBuilder(len+1000); // pad for surrogates @@ -84,8 +124,36 @@ private String generateRandom(int len) } } return sb.toString(); - } + } + + private void _testMediumStrings(boolean useBinary, int length) throws Exception + { + String text = _generareMediumText(length); + StringWriter sw = new StringWriter(); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + JsonGenerator gen = useBinary ? FACTORY.createGenerator(bytes) + : FACTORY.createGenerator(sw); + gen.writeStartArray(); + gen.writeString(text); + gen.writeEndArray(); + gen.close(); + + String json; + if (useBinary) { + json = bytes.toString("UTF-8"); + } else { + json = sw.toString(); + } + + JsonParser p = FACTORY.createParser(json); + assertToken(JsonToken.START_ARRAY, p.nextToken()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals(text, p.getText()); + assertToken(JsonToken.END_ARRAY, p.nextToken()); + p.close(); + } + private void doTestBasicEscaping(boolean charArray) throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestJsonGenerator.java b/src/test/java/com/fasterxml/jackson/core/json/TestJsonGenerator.java index 820e208e31..2dd03b8060 100644 --- a/src/test/java/com/fasterxml/jackson/core/json/TestJsonGenerator.java +++ b/src/test/java/com/fasterxml/jackson/core/json/TestJsonGenerator.java @@ -11,11 +11,12 @@ public class TestJsonGenerator extends com.fasterxml.jackson.core.BaseTest { + private final JsonFactory JSON_F = new JsonFactory(); + // // // First, tests for primitive (non-structured) values public void testStringWrite() throws Exception { - JsonFactory jf = new JsonFactory(); String[] inputStrings = new String[] { "", "X", "1234567890" }; for (int useReader = 0; useReader < 2; ++useReader) { for (int writeString = 0; writeString < 2; ++writeString) { @@ -24,9 +25,9 @@ public void testStringWrite() throws Exception JsonGenerator gen; ByteArrayOutputStream bout = new ByteArrayOutputStream(); if (useReader != 0) { - gen = jf.createGenerator(new OutputStreamWriter(bout, "UTF-8")); + gen = JSON_F.createGenerator(new OutputStreamWriter(bout, "UTF-8")); } else { - gen = jf.createGenerator(bout, JsonEncoding.UTF8); + gen = JSON_F.createGenerator(bout, JsonEncoding.UTF8); } if (writeString > 0) { gen.writeString(input); @@ -39,7 +40,7 @@ public void testStringWrite() throws Exception } gen.flush(); gen.close(); - JsonParser jp = jf.createParser(new ByteArrayInputStream(bout.toByteArray())); + JsonParser jp = JSON_F.createParser(new ByteArrayInputStream(bout.toByteArray())); JsonToken t = jp.nextToken(); assertNotNull("Document \""+bout.toString("UTF-8")+"\" yielded no tokens", t); @@ -52,16 +53,16 @@ public void testStringWrite() throws Exception } } - public void testIntWrite() throws Exception + public void testIntValueWrite() throws Exception { - doTestIntWrite(false); - doTestIntWrite(true); + doTestIntValueWrite(false); + doTestIntValueWrite(true); } - public void testLongWrite() throws Exception + public void testLongValueWrite() throws Exception { - doTestLongWrite(false); - doTestLongWrite(true); + doTestLongValueWrite(false); + doTestLongValueWrite(true); } public void testBooleanWrite() throws Exception @@ -70,7 +71,7 @@ public void testBooleanWrite() throws Exception boolean state = (i & 1) == 0; boolean pad = (i & 2) == 0; StringWriter sw = new StringWriter(); - JsonGenerator gen = new JsonFactory().createGenerator(sw); + JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeBoolean(state); if (pad) { gen.writeRaw(" "); @@ -95,7 +96,7 @@ public void testNullWrite() for (int i = 0; i < 2; ++i) { boolean pad = (i & 1) == 0; StringWriter sw = new StringWriter(); - JsonGenerator gen = new JsonFactory().createGenerator(sw); + JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeNull(); if (pad) { gen.writeRaw(" "); @@ -120,7 +121,7 @@ public void testRootIntsWrite() throws Exception { StringWriter sw = new StringWriter(); - JsonGenerator gen = new JsonFactory().createGenerator(sw); + JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeNumber(1); gen.writeNumber(2); gen.writeNumber(-13); @@ -144,7 +145,7 @@ public void testFieldValueWrites() throws Exception { StringWriter sw = new StringWriter(); - JsonGenerator gen = new JsonFactory().createGenerator(sw); + JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeStartObject(); gen.writeNumberField("long", 3L); gen.writeNumberField("double", 0.25); @@ -161,7 +162,7 @@ public void testFieldValueWrites() public void testOutputContext() throws Exception { StringWriter sw = new StringWriter(); - JsonGenerator gen = new JsonFactory().createGenerator(sw); + JsonGenerator gen = JSON_F.createGenerator(sw); JsonStreamContext ctxt = gen.getOutputContext(); assertTrue(ctxt.inRoot()); @@ -232,7 +233,7 @@ public void testOutputContext() throws Exception /********************************************************** */ - private void doTestIntWrite(boolean pad) throws Exception + private void doTestIntValueWrite(boolean pad) throws Exception { int[] VALUES = new int[] { 0, 1, -9, 32, -32, 57, 189, 2017, -9999, 13240, 123456, @@ -241,7 +242,7 @@ private void doTestIntWrite(boolean pad) throws Exception for (int i = 0; i < VALUES.length; ++i) { int VALUE = VALUES[i]; StringWriter sw = new StringWriter(); - JsonGenerator gen = new JsonFactory().createGenerator(sw); + JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeNumber(VALUE); if (pad) { gen.writeRaw(" "); @@ -263,8 +264,7 @@ private void doTestIntWrite(boolean pad) throws Exception } } - private void doTestLongWrite(boolean pad) - throws Exception + private void doTestLongValueWrite(boolean pad) throws Exception { long[] VALUES = new long[] { 0L, 1L, -1L, -12005002294L, Long.MIN_VALUE, Long.MAX_VALUE @@ -272,7 +272,7 @@ private void doTestLongWrite(boolean pad) for (int i = 0; i < VALUES.length; ++i) { long VALUE = VALUES[i]; StringWriter sw = new StringWriter(); - JsonGenerator gen = new JsonFactory().createGenerator(sw); + JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeNumber(VALUE); if (pad) { gen.writeRaw(" "); @@ -293,3 +293,4 @@ private void doTestLongWrite(boolean pad) } } } +