[java] directly append strings while writing JSON output
diff --git a/java/src/org/openqa/selenium/json/JsonOutput.java b/java/src/org/openqa/selenium/json/JsonOutput.java
index e62a785..e24fc86 100644
--- a/java/src/org/openqa/selenium/json/JsonOutput.java
+++ b/java/src/org/openqa/selenium/json/JsonOutput.java
@@ -28,11 +28,13 @@
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Deque;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -42,6 +44,7 @@
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.logging.LogLevelMapping;
@@ -106,7 +109,7 @@ public class JsonOutput implements Closeable {
private final Map<Predicate<Class<?>>, DepthAwareConsumer> converters;
private final Appendable appendable;
- private final Consumer<String> appender;
+ private final Consumer<CharSequence> appender;
private final Deque<Node> stack;
private String indent = "";
private String lineSeparator = "\n";
@@ -432,8 +435,8 @@ public void close() {
}
}
- private JsonOutput append(String text) {
- stack.getFirst().write(text);
+ private JsonOutput append(CharSequence... fragments) {
+ stack.getFirst().write(fragments);
return this;
}
@@ -441,26 +444,36 @@ private JsonOutput append(String text) {
* Return a quoted JSON string representing the specified Java object.
*
* @param obj Java object to be represented
- * @return quoted JSON string
+ * @return the quoted JSON string, might be fragmented into chucks
*/
- private String asString(Object obj) {
- StringBuilder toReturn = new StringBuilder("\"");
+ private CharSequence[] asString(Object obj) {
+ List<CharSequence> quoted = new ArrayList<>(3);
+ quoted.add("\"");
- String.valueOf(obj)
- .chars()
- .forEach(
- i -> {
- String escaped = ESCAPES.get(i);
- if (escaped != null) {
- toReturn.append(escaped);
- } else {
- toReturn.append((char) i);
- }
- });
+ String value = String.valueOf(obj);
+ int n = value.length();
+ int last = 0;
- toReturn.append('"');
+ for (int i = 0; i < n; i++) {
+ String escaped = ESCAPES.get(Integer.valueOf(value.charAt(i)));
- return toReturn.toString();
+ if (escaped != null) {
+ // the text until we need to write an escaped char
+ quoted.add(value.subSequence(last, i));
+ last = i + 1;
+
+ // the escaped char
+ quoted.add(escaped);
+ }
+ }
+
+ // the text after the last escaped char, might be the complete string e.g. when processing huge
+ // base64 encoded data
+ quoted.add(value.subSequence(last, n));
+
+ quoted.add("\"");
+
+ return quoted.toArray(CharSequence[]::new);
}
/**
@@ -570,9 +583,9 @@ private abstract class Node {
* a comma and the defined line separator (either {@literal <newline>} or empty string) to
* delimit a new object property or array item.
*
- * @param text text to be appended to the output
+ * @param fragments text to be appended to the output, might be fragmented into multiple parts
*/
- public void write(String text) {
+ public void write(CharSequence... fragments) {
if (isEmpty) {
isEmpty = false;
} else {
@@ -580,7 +593,10 @@ public void write(String text) {
}
appender.accept(indent);
- appender.accept(text);
+
+ for (int i = 0; i < fragments.length; i++) {
+ appender.accept(fragments[i]);
+ }
}
}
@@ -590,16 +606,16 @@ private class Root extends Node {
/**
* Write the specified text to the appender of this JSON output object.
*
- * @param text text to be appended to the output
+ * @param fragments text to be appended to the output, might be fragmented into multiple parts
* @throws JsonException if this {@link JsonOutput} has already been used.
*/
@Override
- public void write(String text) {
+ public void write(CharSequence... fragments) {
if (!isEmpty) {
throw new JsonException("Only allowed to write one value to a json stream");
}
- super.write(text);
+ super.write(fragments);
}
}
@@ -629,17 +645,21 @@ public void name(String name) {
/**
* Write the value of a JSON property to the appender of this JSON output object.
*
- * @param text JSON object property value
+ * @param fragments JSON object property value, might be fragmented into multiple parts
* @throws JsonException if not expecting a JSON property value
*/
@Override
- public void write(String text) {
+ public void write(CharSequence... fragments) {
if (isNameNext) {
- throw new JsonException("Unexpected attempt to write value before name: " + text);
+ throw new JsonException(
+ "Unexpected attempt to write value before name: "
+ + Stream.of(fragments).collect(Collectors.joining()));
}
isNameNext = true;
- appender.accept(text);
+ for (int i = 0; i < fragments.length; i++) {
+ appender.accept(fragments[i]);
+ }
}
}