Replace usage of StringBuilder by AppendableCharSequence for performance reasons

This commit is contained in:
Norman Maurer 2013-12-03 12:04:07 +01:00
parent f9a77b3c83
commit ec8967f9ff
3 changed files with 244 additions and 12 deletions

View File

@ -22,6 +22,7 @@ import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
import io.netty.util.internal.AppendableCharSequence;
import java.util.List; import java.util.List;
@ -111,7 +112,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
private long chunkSize; private long chunkSize;
private int headerSize; private int headerSize;
private int contentRead; private int contentRead;
private final StringBuilder sb = new StringBuilder(128); private final AppendableCharSequence sb = new AppendableCharSequence(128);
/** /**
* The internal state of {@link HttpObjectDecoder}. * The internal state of {@link HttpObjectDecoder}.
@ -330,7 +331,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
* read chunk, read and ignore the CRLF and repeat until 0 * read chunk, read and ignore the CRLF and repeat until 0
*/ */
case READ_CHUNK_SIZE: try { case READ_CHUNK_SIZE: try {
StringBuilder line = readLine(buffer, maxInitialLineLength); AppendableCharSequence line = readLine(buffer, maxInitialLineLength);
int chunkSize = getChunkSize(line.toString()); int chunkSize = getChunkSize(line.toString());
this.chunkSize = chunkSize; this.chunkSize = chunkSize;
if (chunkSize == 0) { if (chunkSize == 0) {
@ -580,7 +581,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
final HttpMessage message = this.message; final HttpMessage message = this.message;
final HttpHeaders headers = message.headers(); final HttpHeaders headers = message.headers();
StringBuilder line = readHeader(buffer); AppendableCharSequence line = readHeader(buffer);
String name = null; String name = null;
String value = null; String value = null;
if (line.length() > 0) { if (line.length() > 0) {
@ -624,7 +625,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
private LastHttpContent readTrailingHeaders(ByteBuf buffer) { private LastHttpContent readTrailingHeaders(ByteBuf buffer) {
headerSize = 0; headerSize = 0;
StringBuilder line = readHeader(buffer); AppendableCharSequence line = readHeader(buffer);
String lastHeader = null; String lastHeader = null;
if (line.length() > 0) { if (line.length() > 0) {
LastHttpContent trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders); LastHttpContent trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);
@ -659,9 +660,9 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
return LastHttpContent.EMPTY_LAST_CONTENT; return LastHttpContent.EMPTY_LAST_CONTENT;
} }
private StringBuilder readHeader(ByteBuf buffer) { private AppendableCharSequence readHeader(ByteBuf buffer) {
StringBuilder sb = this.sb; AppendableCharSequence sb = this.sb;
sb.setLength(0); sb.reset();
int headerSize = this.headerSize; int headerSize = this.headerSize;
loop: loop:
@ -716,9 +717,9 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
return Integer.parseInt(hex, 16); return Integer.parseInt(hex, 16);
} }
private StringBuilder readLine(ByteBuf buffer, int maxLineLength) { private AppendableCharSequence readLine(ByteBuf buffer, int maxLineLength) {
StringBuilder sb = this.sb; AppendableCharSequence sb = this.sb;
sb.setLength(0); sb.reset();
int lineLength = 0; int lineLength = 0;
while (true) { while (true) {
byte nextByte = buffer.readByte(); byte nextByte = buffer.readByte();
@ -745,7 +746,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
} }
} }
private static String[] splitInitialLine(StringBuilder sb) { private static String[] splitInitialLine(AppendableCharSequence sb) {
int aStart; int aStart;
int aEnd; int aEnd;
int bStart; int bStart;
@ -768,7 +769,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
cStart < cEnd? sb.substring(cStart, cEnd) : "" }; cStart < cEnd? sb.substring(cStart, cEnd) : "" };
} }
private static String[] splitHeader(StringBuilder sb) { private static String[] splitHeader(AppendableCharSequence sb) {
final int length = sb.length(); final int length = sb.length();
int nameStart; int nameStart;
int nameEnd; int nameEnd;

View File

@ -0,0 +1,137 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util.internal;
import java.util.Arrays;
public final class AppendableCharSequence implements CharSequence, Appendable {
private char[] chars;
private int pos;
public AppendableCharSequence(int length) {
chars = new char[length];
}
private AppendableCharSequence(char[] chars) {
this.chars = chars;
}
@Override
public int length() {
return pos;
}
@Override
public char charAt(int index) {
if (index > pos) {
throw new IndexOutOfBoundsException();
}
return chars[index];
}
@Override
public AppendableCharSequence subSequence(int start, int end) {
return new AppendableCharSequence(Arrays.copyOfRange(chars, start, end));
}
@Override
public AppendableCharSequence append(char c) {
if (pos == chars.length) {
char[] old = chars;
// double it
int len = old.length << 1;
if (len < 0) {
throw new IllegalStateException();
}
chars = new char[len];
System.arraycopy(old, 0, chars, 0, old.length);
}
chars[pos++] = c;
return this;
}
@Override
public AppendableCharSequence append(CharSequence csq) {
return append(csq, 0, csq.length());
}
@Override
public AppendableCharSequence append(CharSequence csq, int start, int end) {
if (csq.length() < end) {
throw new IndexOutOfBoundsException();
}
int length = end - start;
if (length > chars.length - pos) {
chars = expand(chars, pos + length, pos);
}
if (csq instanceof AppendableCharSequence) {
// Optimize append operations via array copy
AppendableCharSequence seq = (AppendableCharSequence) csq;
char[] src = seq.chars;
System.arraycopy(src, start, chars, pos, length);
pos += length;
return this;
}
for (int i = start; i < end; i++) {
chars[pos++] = csq.charAt(i);
}
return this;
}
/**
* Reset the {@link AppendableCharSequence}. Be aware this will only reset the current internal position and not
* shrink the internal char array.
*/
public void reset() {
pos = 0;
}
@Override
public String toString() {
return new String(chars, 0, pos);
}
/**
* Create a new {@link String} from the given start to end.
*/
public String substring(int start, int end) {
int length = end - start;
if (start > pos || length > pos) {
throw new IndexOutOfBoundsException();
}
return new String(chars, start, length);
}
private static char[] expand(char[] array, int neededSpace, int size) {
int newCapacity = array.length;
do {
// double capacity until it is big enough
newCapacity <<= 1;
if (newCapacity < 0) {
throw new IllegalStateException();
}
} while (neededSpace > newCapacity);
char[] newArray = new char[newCapacity];
System.arraycopy(array, 0, newArray, 0, size);
return newArray;
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util.internal;
import org.junit.Test;
import static org.junit.Assert.*;
public class AppendableCharSequenceTest {
@Test
public void testSimpleAppend() {
testSimpleAppend0(new AppendableCharSequence(128));
}
@Test
public void testAppendString() {
testAppendString0(new AppendableCharSequence(128));
}
@Test
public void testAppendAppendableCharSequence() {
AppendableCharSequence seq = new AppendableCharSequence(128);
String text = "testdata";
AppendableCharSequence seq2 = new AppendableCharSequence(128);
seq2.append(text);
seq.append(seq2);
assertEquals(text, seq.toString());
assertEquals(text.substring(1, text.length() - 2), seq.substring(1, text.length() - 2));
assertEqualsChars(text, seq);
}
@Test
public void testSimpleAppendWithExpand() {
testSimpleAppend0(new AppendableCharSequence(2));
}
@Test
public void testAppendStringWithExpand() {
testAppendString0(new AppendableCharSequence(2));
}
private static void testSimpleAppend0(AppendableCharSequence seq) {
String text = "testdata";
for (int i = 0; i < text.length(); i++) {
seq.append(text.charAt(i));
}
assertEquals(text, seq.toString());
assertEquals(text.substring(1, text.length() - 2), seq.substring(1, text.length() - 2));
assertEqualsChars(text, seq);
seq.reset();
assertEquals(0, seq.length());
}
private static void testAppendString0(AppendableCharSequence seq) {
String text = "testdata";
seq.append(text);
assertEquals(text, seq.toString());
assertEquals(text.substring(1, text.length() - 2), seq.substring(1, text.length() - 2));
assertEqualsChars(text, seq);
seq.reset();
assertEquals(0, seq.length());
}
private static void assertEqualsChars(CharSequence seq1, CharSequence seq2) {
assertEquals(seq1.length(), seq2.length());
for (int i = 0; i < seq1.length(); i++) {
assertEquals(seq1.charAt(i), seq2.charAt(i));
}
}
}