Apply appropriate methods for writing CharSequence into ByteBuf

Motivation:

1. `ByteBuf` contains methods to writing `CharSequence` which optimized for UTF-8 and ASCII encodings. We can also apply optimization for ISO-8859-1.
2. In many places appropriate methods are not used.

Modifications:

1. Apply optimization for ISO-8859-1 encoding in the `ByteBuf#setCharSequence` realizations.
2. Apply appropriate methods for writing `CharSequences` into buffers.

Result:

Reduce overhead from string-to-bytes conversion.
This commit is contained in:
Nikolay Fedorovskikh 2017-06-23 15:00:01 +05:00 committed by Norman Maurer
parent 322fe8ec85
commit ba3616da3e
18 changed files with 74 additions and 105 deletions

View File

@ -671,7 +671,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
ensureWritable(ByteBufUtil.utf8MaxBytes(sequence));
return ByteBufUtil.writeUtf8(this, index, sequence, sequence.length());
}
if (charset.equals(CharsetUtil.US_ASCII)) {
if (charset.equals(CharsetUtil.US_ASCII) || charset.equals(CharsetUtil.ISO_8859_1)) {
int len = sequence.length();
ensureWritable(len);
return ByteBufUtil.writeAscii(this, index, sequence, len);

View File

@ -393,7 +393,7 @@ abstract class AbstractUnpooledSlicedByteBuf extends AbstractDerivedByteBuf {
checkIndex0(index, ByteBufUtil.utf8MaxBytes(sequence));
return ByteBufUtil.writeUtf8(this, idx(index), sequence, sequence.length());
}
if (charset.equals(CharsetUtil.US_ASCII)) {
if (charset.equals(CharsetUtil.US_ASCII) || charset.equals(CharsetUtil.ISO_8859_1)) {
int len = sequence.length();
checkIndex0(index, len);
return ByteBufUtil.writeAscii(this, idx(index), sequence, len);

View File

@ -571,7 +571,7 @@ public final class ByteBufUtil {
// We can use the _set methods as these not need to do any index checks and reference checks.
// This is possible as we called ensureWritable(...) before.
for (int i = 0; i < len; i++) {
buffer._setByte(writerIndex++, (byte) seq.charAt(i));
buffer._setByte(writerIndex++, AsciiString.c2b(seq.charAt(i)));
}
return len;
}
@ -739,6 +739,15 @@ public final class ByteBufUtil {
return v;
}
/**
* Copies the all content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}.
* @param src The source of the data to copy.
* @param dst the destination byte array.
*/
public static void copy(AsciiString src, ByteBuf dst) {
copy(src, 0, dst, src.length());
}
/**
* Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}.
* @param src The source of the data to copy.

View File

@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.handler.codec.DateFormatter;
import io.netty.handler.codec.Headers;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;
import java.text.ParseException;
import java.util.Calendar;
@ -1148,19 +1149,12 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
return AsciiString.contentEqualsIgnoreCase(name1, name2);
}
static void encode(HttpHeaders headers, ByteBuf buf) throws Exception {
Iterator<Entry<CharSequence, CharSequence>> iter = headers.iteratorCharSequence();
while (iter.hasNext()) {
Entry<CharSequence, CharSequence> header = iter.next();
HttpHeadersEncoder.encoderHeader(header.getKey(), header.getValue(), buf);
}
}
@Deprecated
public static void encodeAscii(CharSequence seq, ByteBuf buf) {
if (seq instanceof AsciiString) {
ByteBufUtil.copy((AsciiString) seq, 0, buf, seq.length());
} else {
HttpUtil.encodeAscii0(seq, buf);
buf.writeCharSequence(seq, CharsetUtil.US_ASCII);
}
}

View File

@ -19,7 +19,7 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.AsciiString;
import static io.netty.util.AsciiString.c2b;
import io.netty.util.CharsetUtil;
final class HttpHeadersEncoder {
@ -32,28 +32,22 @@ final class HttpHeadersEncoder {
final int entryLen = nameLen + valueLen + 4;
buf.ensureWritable(entryLen);
int offset = buf.writerIndex();
writeAscii(buf, offset, name, nameLen);
writeAscii(buf, offset, name);
offset += nameLen;
buf.setByte(offset ++, ':');
buf.setByte(offset ++, ' ');
writeAscii(buf, offset, value, valueLen);
writeAscii(buf, offset, value);
offset += valueLen;
buf.setByte(offset ++, '\r');
buf.setByte(offset ++, '\n');
buf.writerIndex(offset);
}
private static void writeAscii(ByteBuf buf, int offset, CharSequence value, int valueLen) {
private static void writeAscii(ByteBuf buf, int offset, CharSequence value) {
if (value instanceof AsciiString) {
ByteBufUtil.copy((AsciiString) value, 0, buf, offset, valueLen);
ByteBufUtil.copy((AsciiString) value, 0, buf, offset, value.length());
} else {
writeCharSequence(buf, offset, value, valueLen);
}
}
private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) {
for (int i = 0; i < valueLen; ++i) {
buf.setByte(offset ++, c2b(value.charAt(i)));
buf.setCharSequence(offset, value, CharsetUtil.US_ASCII);
}
}
}

View File

@ -163,9 +163,9 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
private void encodeChunkedContent(ChannelHandlerContext ctx, Object msg, long contentLength, List<Object> out) {
if (contentLength > 0) {
byte[] length = Long.toHexString(contentLength).getBytes(CharsetUtil.US_ASCII);
ByteBuf buf = ctx.alloc().buffer(length.length + 2);
buf.writeBytes(length);
String lengthHex = Long.toHexString(contentLength);
ByteBuf buf = ctx.alloc().buffer(lengthHex.length() + 2);
buf.writeCharSequence(lengthHex, CharsetUtil.US_ASCII);
buf.writeBytes(CRLF);
out.add(buf);
out.add(encodeAndRetain(msg));
@ -239,7 +239,7 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
@Deprecated
protected static void encodeAscii(String s, ByteBuf buf) {
HttpUtil.encodeAscii0(s, buf);
buf.writeCharSequence(s, CharsetUtil.US_ASCII);
}
protected abstract void encodeInitialLine(ByteBuf buf, H message) throws Exception;

View File

@ -17,7 +17,6 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;
import static io.netty.handler.codec.http.HttpConstants.SP;
@ -37,8 +36,7 @@ public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequest> {
@Override
protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception {
AsciiString method = request.method().asciiName();
ByteBufUtil.copy(method, method.arrayOffset(), buf, method.length());
ByteBufUtil.copy(request.method().asciiName(), buf);
buf.writeByte(SP);
// Add / as absolute path if no is present.
@ -71,7 +69,7 @@ public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequest> {
}
}
buf.writeBytes(uri.getBytes(CharsetUtil.UTF_8));
buf.writeCharSequence(uri, CharsetUtil.UTF_8);
buf.writeByte(SP);
request.protocolVersion().encode(buf);

View File

@ -17,6 +17,7 @@ package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpConstants.SP;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor;
import io.netty.util.CharsetUtil;
@ -667,8 +668,8 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
@Override
public String toString() {
return new StringBuilder(reasonPhrase.length() + 5)
.append(code)
return new StringBuilder(reasonPhrase.length() + 4)
.append(codeAsText)
.append(' ')
.append(reasonPhrase)
.toString();
@ -676,9 +677,9 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
void encode(ByteBuf buf) {
if (bytes == null) {
HttpUtil.encodeAscii0(String.valueOf(code()), buf);
ByteBufUtil.copy(codeAsText, buf);
buf.writeByte(SP);
HttpUtil.encodeAscii0(String.valueOf(reasonPhrase()), buf);
buf.writeCharSequence(reasonPhrase, CharsetUtil.US_ASCII);
} else {
buf.writeBytes(bytes);
}

View File

@ -15,7 +15,6 @@
*/
package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;
@ -353,7 +352,7 @@ public final class HttpUtil {
* Fetch charset from message's Content-Type header.
*
* @param message entity to fetch Content-Type header from
* @return the charset from message's Content-Type header or {@link io.netty.util.CharsetUtil#ISO_8859_1}
* @return the charset from message's Content-Type header or {@link CharsetUtil#ISO_8859_1}
* if charset is not presented or unparsable
*/
public static Charset getCharset(HttpMessage message) {
@ -364,7 +363,7 @@ public final class HttpUtil {
* Fetch charset from Content-Type header value.
*
* @param contentTypeValue Content-Type header value to parse
* @return the charset from message's Content-Type header or {@link io.netty.util.CharsetUtil#ISO_8859_1}
* @return the charset from message's Content-Type header or {@link CharsetUtil#ISO_8859_1}
* if charset is not presented or unparsable
*/
public static Charset getCharset(CharSequence contentTypeValue) {
@ -379,7 +378,7 @@ public final class HttpUtil {
* Fetch charset from message's Content-Type header.
*
* @param message entity to fetch Content-Type header from
* @param defaultCharset result to use in case of empty, incorrect or doesn't conain required part header value
* @param defaultCharset result to use in case of empty, incorrect or doesn't contain required part header value
* @return the charset from message's Content-Type header or {@code defaultCharset}
* if charset is not presented or unparsable
*/
@ -406,7 +405,7 @@ public final class HttpUtil {
if (charsetCharSequence != null) {
try {
return Charset.forName(charsetCharSequence.toString());
} catch (UnsupportedCharsetException unsupportedException) {
} catch (UnsupportedCharsetException ignored) {
return defaultCharset;
}
} else {
@ -520,15 +519,4 @@ public final class HttpUtil {
return contentTypeValue.length() > 0 ? contentTypeValue : null;
}
}
static void encodeAscii0(CharSequence seq, ByteBuf buf) {
int length = seq.length();
for (int i = 0 ; i < length; i++) {
buf.writeByte(c2b(seq.charAt(i)));
}
}
private static byte c2b(char c) {
return c > 255 ? (byte) '?' : (byte) c;
}
}

View File

@ -264,7 +264,7 @@ public class HttpVersion implements Comparable<HttpVersion> {
void encode(ByteBuf buf) {
if (bytes == null) {
HttpUtil.encodeAscii0(text, buf);
buf.writeCharSequence(text, CharsetUtil.US_ASCII);
} else {
buf.writeBytes(bytes);
}

View File

@ -18,7 +18,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.StringUtil;
/**
* Web Socket Frame for closing the connection
@ -75,15 +75,14 @@ public class CloseWebSocketFrame extends WebSocketFrame {
}
private static ByteBuf newBinaryData(int statusCode, String reasonText) {
byte[] reasonBytes = EmptyArrays.EMPTY_BYTES;
if (reasonText != null) {
reasonBytes = reasonText.getBytes(CharsetUtil.UTF_8);
if (reasonText == null) {
reasonText = StringUtil.EMPTY_STRING;
}
ByteBuf binaryData = Unpooled.buffer(2 + reasonBytes.length);
ByteBuf binaryData = Unpooled.buffer(2 + reasonText.length());
binaryData.writeShort(statusCode);
if (reasonBytes.length > 0) {
binaryData.writeBytes(reasonBytes);
if (!reasonText.isEmpty()) {
binaryData.writeCharSequence(reasonText, CharsetUtil.UTF_8);
}
binaryData.readerIndex(0);

View File

@ -19,9 +19,9 @@ import static io.netty.handler.codec.http.HttpConstants.CR;
import static io.netty.handler.codec.http.HttpConstants.LF;
import static io.netty.handler.codec.http.HttpConstants.SP;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.handler.codec.UnsupportedMessageTypeException;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpObjectEncoder;
import io.netty.handler.codec.http.HttpRequest;
@ -50,22 +50,19 @@ public class RtspEncoder extends HttpObjectEncoder<HttpMessage> {
throws Exception {
if (message instanceof HttpRequest) {
HttpRequest request = (HttpRequest) message;
HttpHeaders.encodeAscii(request.method().toString(), buf);
ByteBufUtil.copy(request.method().asciiName(), buf);
buf.writeByte(SP);
buf.writeBytes(request.uri().getBytes(CharsetUtil.UTF_8));
buf.writeCharSequence(request.uri(), CharsetUtil.UTF_8);
buf.writeByte(SP);
HttpHeaders.encodeAscii(request.protocolVersion().toString(), buf);
buf.writeCharSequence(request.protocolVersion().toString(), CharsetUtil.US_ASCII);
buf.writeBytes(CRLF);
} else if (message instanceof HttpResponse) {
HttpResponse response = (HttpResponse) message;
HttpHeaders.encodeAscii(response.protocolVersion().toString(),
buf);
buf.writeCharSequence(response.protocolVersion().toString(), CharsetUtil.US_ASCII);
buf.writeByte(SP);
buf.writeBytes(String.valueOf(response.status().code())
.getBytes(CharsetUtil.US_ASCII));
ByteBufUtil.copy(response.status().codeAsText(), buf);
buf.writeByte(SP);
HttpHeaders.encodeAscii(String.valueOf(response.status().reasonPhrase()),
buf);
buf.writeCharSequence(response.status().reasonPhrase(), CharsetUtil.US_ASCII);
buf.writeBytes(CRLF);
} else {
throw new UnsupportedMessageTypeException("Unsupported type "

View File

@ -76,8 +76,8 @@ public final class SocksAuthRequest extends SocksRequest {
public void encodeAsByteBuf(ByteBuf byteBuf) {
byteBuf.writeByte(SUBNEGOTIATION_VERSION.byteValue());
byteBuf.writeByte(username.length());
byteBuf.writeBytes(username.getBytes(CharsetUtil.US_ASCII));
byteBuf.writeCharSequence(username, CharsetUtil.US_ASCII);
byteBuf.writeByte(password.length());
byteBuf.writeBytes(password.getBytes(CharsetUtil.US_ASCII));
byteBuf.writeCharSequence(password, CharsetUtil.US_ASCII);
}
}

View File

@ -123,7 +123,7 @@ public final class SocksCmdRequest extends SocksRequest {
case DOMAIN: {
byteBuf.writeByte(host.length());
byteBuf.writeBytes(host.getBytes(CharsetUtil.US_ASCII));
byteBuf.writeCharSequence(host, CharsetUtil.US_ASCII);
byteBuf.writeShort(port);
break;
}

View File

@ -118,11 +118,11 @@ public final class SocksCmdResponse extends SocksResponse {
}
/**
* Returns host that is used as a parameter in {@link io.netty.handler.codec.socks.SocksCmdType}.
* Returns host that is used as a parameter in {@link SocksCmdType}.
* Host (BND.ADDR field in response) is address that server used when connecting to the target host.
* This is typically different from address which client uses to connect to the SOCKS server.
*
* @return host that is used as a parameter in {@link io.netty.handler.codec.socks.SocksCmdType}
* @return host that is used as a parameter in {@link SocksCmdType}
* or null when there was no host specified during response construction
*/
public String host() {
@ -134,10 +134,10 @@ public final class SocksCmdResponse extends SocksResponse {
}
/**
* Returns port that is used as a parameter in {@link io.netty.handler.codec.socks.SocksCmdType}.
* Returns port that is used as a parameter in {@link SocksCmdType}.
* Port (BND.PORT field in response) is port that the server assigned to connect to the target host.
*
* @return port that is used as a parameter in {@link io.netty.handler.codec.socks.SocksCmdType}
* @return port that is used as a parameter in {@link SocksCmdType}
*/
public int port() {
return port;
@ -158,11 +158,14 @@ public final class SocksCmdResponse extends SocksResponse {
break;
}
case DOMAIN: {
byte[] hostContent = host == null ?
DOMAIN_ZEROED : host.getBytes(CharsetUtil.US_ASCII);
byteBuf.writeByte(hostContent.length); // domain length
byteBuf.writeBytes(hostContent); // domain value
byteBuf.writeShort(port); // port value
if (host != null) {
byteBuf.writeByte(host.length());
byteBuf.writeCharSequence(host, CharsetUtil.US_ASCII);
} else {
byteBuf.writeByte(DOMAIN_ZEROED.length);
byteBuf.writeBytes(DOMAIN_ZEROED);
}
byteBuf.writeShort(port);
break;
}
case IPv6: {

View File

@ -41,9 +41,8 @@ public interface Socks5AddressEncoder {
}
} else if (typeVal == Socks5AddressType.DOMAIN.byteValue()) {
if (addrValue != null) {
byte[] bndAddr = addrValue.getBytes(CharsetUtil.US_ASCII);
out.writeByte(bndAddr.length);
out.writeBytes(bndAddr);
out.writeByte(addrValue.length());
out.writeCharSequence(addrValue, CharsetUtil.US_ASCII);
} else {
out.writeByte(1);
out.writeByte(0);

View File

@ -64,7 +64,7 @@ public class StompSubframeEncoder extends MessageToMessageEncoder<StompSubframe>
private static ByteBuf encodeFrame(StompHeadersSubframe frame, ChannelHandlerContext ctx) {
ByteBuf buf = ctx.alloc().buffer();
buf.writeBytes(frame.command().toString().getBytes(CharsetUtil.US_ASCII));
buf.writeCharSequence(frame.command().toString(), CharsetUtil.US_ASCII);
buf.writeByte(StompConstants.LF);
AsciiHeadersEncoder headersEncoder = new AsciiHeadersEncoder(buf, SeparatorType.COLON, NewlineType.LF);
for (Entry<CharSequence, CharSequence> entry : frame.headers()) {

View File

@ -22,6 +22,7 @@ import java.util.Map.Entry;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;
public final class AsciiHeadersEncoder {
@ -86,7 +87,7 @@ public final class AsciiHeadersEncoder {
final int entryLen = nameLen + valueLen + 4;
int offset = buf.writerIndex();
buf.ensureWritable(entryLen);
writeAscii(buf, offset, name, nameLen);
writeAscii(buf, offset, name);
offset += nameLen;
switch (separatorType) {
@ -101,7 +102,7 @@ public final class AsciiHeadersEncoder {
throw new Error();
}
writeAscii(buf, offset, value, valueLen);
writeAscii(buf, offset, value);
offset += valueLen;
switch (newlineType) {
@ -119,25 +120,11 @@ public final class AsciiHeadersEncoder {
buf.writerIndex(offset);
}
private static void writeAscii(ByteBuf buf, int offset, CharSequence value, int valueLen) {
private static void writeAscii(ByteBuf buf, int offset, CharSequence value) {
if (value instanceof AsciiString) {
writeAsciiString(buf, offset, (AsciiString) value, valueLen);
ByteBufUtil.copy((AsciiString) value, 0, buf, offset, value.length());
} else {
writeCharSequence(buf, offset, value, valueLen);
buf.setCharSequence(offset, value, CharsetUtil.US_ASCII);
}
}
private static void writeAsciiString(ByteBuf buf, int offset, AsciiString value, int valueLen) {
ByteBufUtil.copy(value, 0, buf, offset, valueLen);
}
private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) {
for (int i = 0; i < valueLen; i ++) {
buf.setByte(offset ++, c2b(value.charAt(i)));
}
}
private static int c2b(char ch) {
return ch < 256? (byte) ch : '?';
}
}