From a00be62b046d989c94879f03711e82b5eedfdd10 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 20 Dec 2018 21:33:50 +0100 Subject: [PATCH] Calculating capacity before allocation. This commit optimizes the `CharSequenceEncoder` to allocate `DataBuffer` instances with a predicted capacity. Issue: SPR-17558 --- .../core/codec/CharSequenceEncoder.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java index f2e1642ed0..25ae5a517d 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java @@ -20,6 +20,8 @@ import java.nio.charset.Charset; import java.nio.charset.CoderMalfunctionError; import java.nio.charset.StandardCharsets; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -49,6 +51,9 @@ public final class CharSequenceEncoder extends AbstractEncoder { */ public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + private final ConcurrentMap charsetToMaxBytesPerChar = + new ConcurrentHashMap<>(3); + private CharSequenceEncoder(MimeType... mimeTypes) { super(mimeTypes); @@ -76,7 +81,8 @@ public final class CharSequenceEncoder extends AbstractEncoder { }); } boolean release = true; - DataBuffer dataBuffer = bufferFactory.allocateBuffer(); + int capacity = calculateCapacity(charSequence, charset); + DataBuffer dataBuffer = bufferFactory.allocateBuffer(capacity); try { dataBuffer.write(charSequence, charset); release = false; @@ -93,6 +99,13 @@ public final class CharSequenceEncoder extends AbstractEncoder { }); } + private int calculateCapacity(CharSequence sequence, Charset charset) { + float maxBytesPerChar = this.charsetToMaxBytesPerChar + .computeIfAbsent(charset, cs -> cs.newEncoder().maxBytesPerChar()); + float maxBytesForSequence = sequence.length() * maxBytesPerChar; + return (int) Math.ceil(maxBytesForSequence); + } + private Charset getCharset(@Nullable MimeType mimeType) { if (mimeType != null && mimeType.getCharset() != null) { return mimeType.getCharset();