From 27ff15319c2e566c44e333b9bd59cf210c130c2f Mon Sep 17 00:00:00 2001 From: Henning Rohlfs Date: Wed, 3 Jan 2018 13:08:48 +0100 Subject: [PATCH] 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. --- .../codec/stomp/StompSubframeDecoder.java | 74 ++++++++++++++----- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java index 3ce55f21cb..b4d6a14958 100644 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java +++ b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java @@ -196,7 +196,7 @@ public class StompSubframeDecoder extends ReplayingDecoder { } 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 { } 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 { } } - 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 { } } + 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;