|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2018 the original author or authors. |
|
|
|
* Copyright 2002-2019 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -25,7 +25,6 @@ import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ConcurrentMap; |
|
|
|
import java.util.concurrent.ConcurrentMap; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.reactivestreams.Publisher; |
|
|
|
import org.reactivestreams.Publisher; |
|
|
|
import reactor.core.publisher.Flux; |
|
|
|
import reactor.core.publisher.Flux; |
|
|
|
@ -88,14 +87,14 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Flux<String> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType, |
|
|
|
public Flux<String> decode(Publisher<DataBuffer> input, ResolvableType elementType, |
|
|
|
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { |
|
|
|
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { |
|
|
|
|
|
|
|
|
|
|
|
List<byte[]> delimiterBytes = getDelimiterBytes(mimeType); |
|
|
|
List<byte[]> delimiterBytes = getDelimiterBytes(mimeType); |
|
|
|
|
|
|
|
|
|
|
|
Flux<DataBuffer> inputFlux = Flux.from(inputStream) |
|
|
|
Flux<DataBuffer> inputFlux = Flux.from(input) |
|
|
|
.flatMapIterable(dataBuffer -> splitOnDelimiter(dataBuffer, delimiterBytes)) |
|
|
|
.flatMapIterable(buffer -> splitOnDelimiter(buffer, delimiterBytes)) |
|
|
|
.bufferUntil(StringDecoder::isEndFrame) |
|
|
|
.bufferUntil(buffer -> buffer == END_FRAME) |
|
|
|
.map(StringDecoder::joinUntilEndFrame) |
|
|
|
.map(StringDecoder::joinUntilEndFrame) |
|
|
|
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); |
|
|
|
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); |
|
|
|
|
|
|
|
|
|
|
|
@ -103,51 +102,60 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private List<byte[]> getDelimiterBytes(@Nullable MimeType mimeType) { |
|
|
|
private List<byte[]> getDelimiterBytes(@Nullable MimeType mimeType) { |
|
|
|
return this.delimitersCache.computeIfAbsent(getCharset(mimeType), |
|
|
|
return this.delimitersCache.computeIfAbsent(getCharset(mimeType), charset -> { |
|
|
|
charset -> this.delimiters.stream() |
|
|
|
List<byte[]> list = new ArrayList<>(); |
|
|
|
.map(s -> s.getBytes(charset)) |
|
|
|
for (String delimiter : this.delimiters) { |
|
|
|
.collect(Collectors.toList())); |
|
|
|
byte[] bytes = delimiter.getBytes(charset); |
|
|
|
|
|
|
|
list.add(bytes); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return list; |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Split the given data buffer on delimiter boundaries. |
|
|
|
* Split the given data buffer on delimiter boundaries. |
|
|
|
* The returned Flux contains an {@link #END_FRAME} buffer after each delimiter. |
|
|
|
* The returned Flux contains an {@link #END_FRAME} buffer after each delimiter. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private List<DataBuffer> splitOnDelimiter(DataBuffer dataBuffer, List<byte[]> delimiterBytes) { |
|
|
|
private List<DataBuffer> splitOnDelimiter(DataBuffer buffer, List<byte[]> delimiterBytes) { |
|
|
|
List<DataBuffer> frames = new ArrayList<>(); |
|
|
|
List<DataBuffer> frames = new ArrayList<>(); |
|
|
|
do { |
|
|
|
try { |
|
|
|
int length = Integer.MAX_VALUE; |
|
|
|
do { |
|
|
|
byte[] matchingDelimiter = null; |
|
|
|
int length = Integer.MAX_VALUE; |
|
|
|
for (byte[] delimiter : delimiterBytes) { |
|
|
|
byte[] matchingDelimiter = null; |
|
|
|
int index = indexOf(dataBuffer, delimiter); |
|
|
|
for (byte[] delimiter : delimiterBytes) { |
|
|
|
if (index >= 0 && index < length) { |
|
|
|
int index = indexOf(buffer, delimiter); |
|
|
|
length = index; |
|
|
|
if (index >= 0 && index < length) { |
|
|
|
matchingDelimiter = delimiter; |
|
|
|
length = index; |
|
|
|
|
|
|
|
matchingDelimiter = delimiter; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
DataBuffer frame; |
|
|
|
DataBuffer frame; |
|
|
|
int readPosition = buffer.readPosition(); |
|
|
|
int readPosition = dataBuffer.readPosition(); |
|
|
|
if (matchingDelimiter != null) { |
|
|
|
if (matchingDelimiter != null) { |
|
|
|
frame = this.stripDelimiter ? |
|
|
|
if (this.stripDelimiter) { |
|
|
|
buffer.slice(readPosition, length) : |
|
|
|
frame = dataBuffer.slice(readPosition, length); |
|
|
|
buffer.slice(readPosition, length + matchingDelimiter.length); |
|
|
|
|
|
|
|
buffer.readPosition(readPosition + length + matchingDelimiter.length); |
|
|
|
|
|
|
|
frames.add(DataBufferUtils.retain(frame)); |
|
|
|
|
|
|
|
frames.add(END_FRAME); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
frame = dataBuffer.slice(readPosition, length + matchingDelimiter.length); |
|
|
|
frame = buffer.slice(readPosition, buffer.readableByteCount()); |
|
|
|
|
|
|
|
buffer.readPosition(readPosition + buffer.readableByteCount()); |
|
|
|
|
|
|
|
frames.add(DataBufferUtils.retain(frame)); |
|
|
|
} |
|
|
|
} |
|
|
|
dataBuffer.readPosition(readPosition + length + matchingDelimiter.length); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
frames.add(DataBufferUtils.retain(frame)); |
|
|
|
|
|
|
|
frames.add(END_FRAME); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
while (buffer.readableByteCount() > 0); |
|
|
|
frame = dataBuffer.slice(readPosition, dataBuffer.readableByteCount()); |
|
|
|
} |
|
|
|
dataBuffer.readPosition(readPosition + dataBuffer.readableByteCount()); |
|
|
|
catch (Throwable ex) { |
|
|
|
frames.add(DataBufferUtils.retain(frame)); |
|
|
|
for (DataBuffer frame : frames) { |
|
|
|
|
|
|
|
DataBufferUtils.release(frame); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
throw ex; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
finally { |
|
|
|
|
|
|
|
DataBufferUtils.release(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
while (dataBuffer.readableByteCount() > 0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DataBufferUtils.release(dataBuffer); |
|
|
|
|
|
|
|
return frames; |
|
|
|
return frames; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -155,44 +163,38 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> { |
|
|
|
* Find the given delimiter in the given data buffer. |
|
|
|
* Find the given delimiter in the given data buffer. |
|
|
|
* @return the index of the delimiter, or -1 if not found. |
|
|
|
* @return the index of the delimiter, or -1 if not found. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private static int indexOf(DataBuffer dataBuffer, byte[] delimiter) { |
|
|
|
private static int indexOf(DataBuffer buffer, byte[] delimiter) { |
|
|
|
for (int i = dataBuffer.readPosition(); i < dataBuffer.writePosition(); i++) { |
|
|
|
for (int i = buffer.readPosition(); i < buffer.writePosition(); i++) { |
|
|
|
int dataBufferPos = i; |
|
|
|
int bufferPos = i; |
|
|
|
int delimiterPos = 0; |
|
|
|
int delimiterPos = 0; |
|
|
|
while (delimiterPos < delimiter.length) { |
|
|
|
while (delimiterPos < delimiter.length) { |
|
|
|
if (dataBuffer.getByte(dataBufferPos) != delimiter[delimiterPos]) { |
|
|
|
if (buffer.getByte(bufferPos) != delimiter[delimiterPos]) { |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
dataBufferPos++; |
|
|
|
bufferPos++; |
|
|
|
if (dataBufferPos == dataBuffer.writePosition() && |
|
|
|
boolean endOfBuffer = bufferPos == buffer.writePosition(); |
|
|
|
delimiterPos != delimiter.length - 1) { |
|
|
|
boolean endOfDelimiter = delimiterPos == delimiter.length - 1; |
|
|
|
|
|
|
|
if (endOfBuffer && !endOfDelimiter) { |
|
|
|
return -1; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
delimiterPos++; |
|
|
|
delimiterPos++; |
|
|
|
} |
|
|
|
} |
|
|
|
if (delimiterPos == delimiter.length) { |
|
|
|
if (delimiterPos == delimiter.length) { |
|
|
|
return i - dataBuffer.readPosition(); |
|
|
|
return i - buffer.readPosition(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return -1; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Check whether the given buffer is {@link #END_FRAME}. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private static boolean isEndFrame(DataBuffer dataBuffer) { |
|
|
|
|
|
|
|
return dataBuffer == END_FRAME; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Join the given list of buffers into a single buffer. |
|
|
|
* Join the given list of buffers into a single buffer. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private static DataBuffer joinUntilEndFrame(List<DataBuffer> dataBuffers) { |
|
|
|
private static DataBuffer joinUntilEndFrame(List<DataBuffer> dataBuffers) { |
|
|
|
if (!dataBuffers.isEmpty()) { |
|
|
|
if (!dataBuffers.isEmpty()) { |
|
|
|
int lastIdx = dataBuffers.size() - 1; |
|
|
|
int lastIdx = dataBuffers.size() - 1; |
|
|
|
if (isEndFrame(dataBuffers.get(lastIdx))) { |
|
|
|
if (dataBuffers.get(lastIdx) == END_FRAME) { |
|
|
|
dataBuffers.remove(lastIdx); |
|
|
|
dataBuffers.remove(lastIdx); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|