Skip to content

Commit

Permalink
Reduce memory allocations in StompSubframeDecoder.readHeaders
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
xehonk authored and normanmaurer committed Jan 23, 2018
1 parent 4c1e0f5 commit 27ff153
Showing 1 changed file with 55 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) t
}

private StompCommand readCommand(ByteBuf in) {
String commandStr = readLine(in, maxLineLength);
String commandStr = readLine(in, 16);
StompCommand command = null;
try {
command = StompCommand.valueOf(commandStr);
Expand All @@ -218,18 +218,11 @@ private StompCommand readCommand(ByteBuf in) {
}

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;
Expand Down Expand Up @@ -266,28 +259,71 @@ private static void skipControlCharacters(ByteBuf buffer) {
}
}

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);
}
}
}

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;
Expand Down

0 comments on commit 27ff153

Please sign in to comment.