Reduce memory allocations in StompSubframeDecoder.readHeaders

Motivation:
When decoding stomp frames a lot of unnecessary character arrays are created when parsing headers.
For every header, an array is created to read the line into and then more when splitting the line at the colon.

Modifications:
Parse key and value of a header while reading the line instead of afterwards.
Reuse a single AppendableCharSequence.
Reduce initial size of AppendableCharSequence when reading the command as it is expected to be short.

Result:
Allocations when parsing stomp frames have dropped significantly.
This commit is contained in:
Henning Rohlfs 2018-01-03 13:08:48 +01:00 committed by Norman Maurer
parent 4c1e0f596a
commit 27ff15319c

View File

@ -196,7 +196,7 @@ public class StompSubframeDecoder extends ReplayingDecoder<State> {
}
private StompCommand readCommand(ByteBuf in) {
String commandStr = readLine(in, maxLineLength);
String commandStr = readLine(in, 16);
StompCommand command = null;
try {
command = StompCommand.valueOf(commandStr);
@ -218,18 +218,11 @@ public class StompSubframeDecoder extends ReplayingDecoder<State> {
}
private State readHeaders(ByteBuf buffer, StompHeaders headers) {
AppendableCharSequence buf = new AppendableCharSequence(128);
for (;;) {
String line = readLine(buffer, maxLineLength);
if (!line.isEmpty()) {
String[] split = line.split(":");
if (split.length == 2) {
headers.add(split[0], split[1]);
} else if (validateHeaders) {
throw new IllegalArgumentException("a header value or name contains a prohibited character ':'" +
", " + line);
}
} else {
if (headers.contains(StompHeaders.CONTENT_LENGTH)) {
boolean headerRead = readHeader(headers, buf, buffer);
if (!headerRead) {
if (headers.contains(StompHeaders.CONTENT_LENGTH)) {
contentLength = getContentLength(headers, 0);
if (contentLength == 0) {
return State.FINALIZE_FRAME_READ;
@ -266,21 +259,18 @@ public class StompSubframeDecoder extends ReplayingDecoder<State> {
}
}
private static String readLine(ByteBuf buffer, int maxLineLength) {
AppendableCharSequence buf = new AppendableCharSequence(128);
private String readLine(ByteBuf buffer, int initialBufferSize) {
AppendableCharSequence buf = new AppendableCharSequence(initialBufferSize);
int lineLength = 0;
for (;;) {
byte nextByte = buffer.readByte();
if (nextByte == StompConstants.CR) {
nextByte = buffer.readByte();
if (nextByte == StompConstants.LF) {
return buf.toString();
}
//do nothing
} else if (nextByte == StompConstants.LF) {
return buf.toString();
} else {
if (lineLength >= maxLineLength) {
throw new TooLongFrameException("An STOMP line is larger than " + maxLineLength + " bytes.");
invalidLineLength();
}
lineLength ++;
buf.append((char) nextByte);
@ -288,6 +278,52 @@ public class StompSubframeDecoder extends ReplayingDecoder<State> {
}
}
private boolean readHeader(StompHeaders headers, AppendableCharSequence buf, ByteBuf buffer) {
buf.reset();
int lineLength = 0;
String key = null;
boolean valid = false;
for (;;) {
byte nextByte = buffer.readByte();
if (nextByte == StompConstants.COLON && key == null) {
key = buf.toString();
valid = true;
buf.reset();
} else if (nextByte == StompConstants.CR) {
//do nothing
} else if (nextByte == StompConstants.LF) {
if (key == null && lineLength == 0) {
return false;
} else if (valid) {
headers.add(key, buf.toString());
} else if (validateHeaders) {
invalidHeader(key, buf.toString());
}
return true;
} else {
if (lineLength >= maxLineLength) {
invalidLineLength();
}
if (nextByte == StompConstants.COLON && key != null) {
valid = false;
}
lineLength ++;
buf.append((char) nextByte);
}
}
}
private void invalidHeader(String key, String value) {
String line = key != null ? key + ":" + value : value;
throw new IllegalArgumentException("a header value or name contains a prohibited character ':'"
+ ", " + line);
}
private void invalidLineLength() {
throw new TooLongFrameException("An STOMP line is larger than " + maxLineLength + " bytes.");
}
private void resetDecoder() {
checkpoint(State.SKIP_CONTROL_CHARACTERS);
contentLength = -1;