From 8fe3d97d8dc6df393c08d345e17834a4c3c13caa Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 15 Jul 2014 10:22:40 +0200 Subject: [PATCH] [#2656] Minimize ByteBuf.writeBytes(...) calls by pack the separator into the HttpHeaderEntity. Motivation: Currently we do 4 ByteBuf.writeBytes(...) calls per header line. This is can be improved. Modification: Introduce two new HttpHeaders methods to allow create HttpHeaderEntity which contains the separator. With this we can minimize it to 2 ByteBuf.writeBytes(...) calls per header line Result: Performance improvement. --- .../handler/codec/http/HttpHeaderEntity.java | 24 ++++++++++-- .../netty/handler/codec/http/HttpHeaders.java | 37 ++++++++++++++++--- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderEntity.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderEntity.java index c667782bc9..3989b41cc2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderEntity.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderEntity.java @@ -23,11 +23,25 @@ final class HttpHeaderEntity implements CharSequence { private final String name; private final int hash; private final byte[] bytes; + private final int separatorLen; public HttpHeaderEntity(String name) { + this(name, null); + } + + public HttpHeaderEntity(String name, byte[] separator) { this.name = name; hash = HttpHeaders.hash(name); - bytes = name.getBytes(CharsetUtil.US_ASCII); + byte[] nameBytes = name.getBytes(CharsetUtil.US_ASCII); + if (separator == null) { + bytes = nameBytes; + separatorLen = 0; + } else { + separatorLen = separator.length; + bytes = new byte[nameBytes.length + separator.length]; + System.arraycopy(nameBytes, 0, bytes, 0, nameBytes.length); + System.arraycopy(separator, 0, bytes, nameBytes.length, separator.length); + } } int hash() { @@ -36,11 +50,14 @@ final class HttpHeaderEntity implements CharSequence { @Override public int length() { - return bytes.length; + return bytes.length - separatorLen; } @Override public char charAt(int index) { + if ((bytes.length - separatorLen) <= index) { + throw new IndexOutOfBoundsException(); + } return (char) bytes[index]; } @@ -54,7 +71,8 @@ final class HttpHeaderEntity implements CharSequence { return name; } - void encode(ByteBuf buf) { + boolean encode(ByteBuf buf) { buf.writeBytes(bytes); + return separatorLen > 0; } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java index fd1ec01df7..cd6ccbf3f0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java @@ -1363,17 +1363,20 @@ public abstract class HttpHeaders implements Iterable> @SuppressWarnings("deprecation") static void encode(CharSequence key, CharSequence value, ByteBuf buf) { - encodeAscii(key, buf); - buf.writeBytes(HEADER_SEPERATOR); - encodeAscii(value, buf); - buf.writeBytes(CRLF); + if (!encodeAscii(key, buf)) { + buf.writeBytes(HEADER_SEPERATOR); + } + if (!encodeAscii(value, buf)) { + buf.writeBytes(CRLF); + } } - public static void encodeAscii(CharSequence seq, ByteBuf buf) { + public static boolean encodeAscii(CharSequence seq, ByteBuf buf) { if (seq instanceof HttpHeaderEntity) { - ((HttpHeaderEntity) seq).encode(buf); + return ((HttpHeaderEntity) seq).encode(buf); } else { encodeAscii0(seq, buf); + return false; } } @@ -1395,6 +1398,28 @@ public abstract class HttpHeaders implements Iterable> return new HttpHeaderEntity(name); } + /** + * Create a new {@link CharSequence} which is optimized for reuse as {@link HttpHeaders} name. + * So if you have a Header name that you want to reuse you should make use of this. + */ + public static CharSequence newNameEntity(String name) { + if (name == null) { + throw new NullPointerException("name"); + } + return new HttpHeaderEntity(name, HEADER_SEPERATOR); + } + + /** + * Create a new {@link CharSequence} which is optimized for reuse as {@link HttpHeaders} value. + * So if you have a Header value that you want to reuse you should make use of this. + */ + public static CharSequence newValueEntity(String name) { + if (name == null) { + throw new NullPointerException("name"); + } + return new HttpHeaderEntity(name, CRLF); + } + protected HttpHeaders() { } /**