ensure all buffers get released again in JsonStreamParserImpl
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
index dede892..b4eb554 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
@@ -74,7 +74,7 @@
//this buffer is used to store current String or Number value in case that
//within the value a buffer boundary is crossed or the string contains escaped characters
private char[] fallBackCopyBuffer;
- private boolean releaseFallBackCopyBufferLength = true;
+ private boolean releaseFallBackCopyBuffer = true;
private int fallBackCopyBufferLength;
// when boundaries of fallBackCopyBuffer have been reached
private List<Buffer> previousFallBackCopyBuffers;
@@ -942,6 +942,11 @@
index += fallBackCopyBufferLength;
releasePreviousFallBackCopyBuffers();
+ if (releaseFallBackCopyBuffer) {
+ valueProvider.release(fallBackCopyBuffer);
+ releaseFallBackCopyBuffer = false;
+ }
+
fallBackCopyBuffer = newBuffer;
fallBackCopyBufferLength = index;
}
@@ -1044,7 +1049,7 @@
}
bufferProvider.release(buffer);
- if (releaseFallBackCopyBufferLength) {
+ if (releaseFallBackCopyBuffer) {
valueProvider.release(fallBackCopyBuffer);
}
releasePreviousFallBackCopyBuffers();
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/HugeStringTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/HugeStringTest.java
deleted file mode 100644
index 72ce072..0000000
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/HugeStringTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.johnzon.core;
-
-import jakarta.json.Json;
-import jakarta.json.JsonReader;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import java.io.StringReader;
-
-@Ignore
-public class HugeStringTest {
- @Test
- public void test() {
- String json = "{\"data\":\"" + "a".repeat(50 * 1024 * 1024 + 1) + "\"}";
-
- // Warmup
- for (int i = 0; i < 10; i++) {
- try (JsonReader reader = Json.createReader(new StringReader(json))) {
- reader.readObject();
- }
- }
-
- long start = System.currentTimeMillis();
- try (JsonReader reader = Json.createReader(new StringReader(json))) {
- reader.readObject();
- }
- System.err.println("Took " + (System.currentTimeMillis() - start) + "ms");
- }
-}
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java
index bf767ab..965d6e9 100644
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java
@@ -18,18 +18,26 @@
*/
package org.apache.johnzon.core;
+import jakarta.json.Json;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonReaderFactory;
+import jakarta.json.spi.JsonProvider;
+import org.junit.Ignore;
import org.junit.Test;
import jakarta.json.stream.JsonParser;
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
public class JsonStreamParserImplTest {
@Test
@@ -94,4 +102,70 @@
asList("START_OBJECT", "KEY_NAME", "VALUE_STRING", "{\"foo\":\"barbar\\barbarbar\"}", "END_OBJECT"),
events);
}
+
+ @Test
+ @Ignore("No real test, just run directly from your IDE")
+ public void largeStringPerformance() {
+ String json = "{\"data\":\"" + "a".repeat(50 * 1024 * 1024 + 1) + "\"}";
+
+ // Warmup
+ for (int i = 0; i < 10; i++) {
+ try (JsonReader reader = Json.createReader(new StringReader(json))) {
+ reader.readObject();
+ }
+ }
+
+ long start = System.currentTimeMillis();
+ try (JsonReader reader = Json.createReader(new StringReader(json))) {
+ reader.readObject();
+ }
+ System.err.println("Took " + (System.currentTimeMillis() - start) + "ms");
+ }
+
+ @Test
+ public void allBuffersReleased() {
+ String json = "{\"data\":\"" + "a".repeat(JsonParserFactoryImpl.DEFAULT_MAX_STRING_LENGTH * 2) + "\"}";
+
+ JsonReaderFactory readerFactory = JsonProvider.provider().createReaderFactory(Map.of(
+ JsonParserFactoryImpl.BUFFER_STRATEGY, TrackingBufferStrategy.class.getName()));
+
+ try (JsonReader reader = readerFactory.createReader(new StringReader(json))) {
+ reader.readObject();
+ }
+
+ assertTrue(TrackingBufferStrategy.TrackingBufferProvider.borrowed.isEmpty());
+ }
+
+ public static class TrackingBufferStrategy implements BufferStrategy {
+ private final BufferStrategy delegate = BufferStrategyFactory.valueOf("BY_INSTANCE");
+
+ @Override
+ public BufferProvider<char[]> newCharProvider(int size) {
+ return new TrackingBufferProvider(delegate.newCharProvider(size));
+ }
+
+ public static class TrackingBufferProvider implements BufferStrategy.BufferProvider<char[]> {
+ protected static List<char[]> borrowed = new ArrayList<>();
+
+ private final BufferStrategy.BufferProvider<char[]> delegate;
+
+ public TrackingBufferProvider(BufferStrategy.BufferProvider<char[]> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public char[] newBuffer() {
+ char[] result = delegate.newBuffer();
+ borrowed.add(result);
+
+ return result;
+ }
+
+ @Override
+ public void release(char[] value) {
+ borrowed.remove(value);
+ delegate.release(value);
+ }
+ }
+ }
}