Replace usage of StringBuilder by AppendableCharSequence for performance reasons
This commit is contained in:
parent
053c512f6d
commit
05c10fae05
@ -22,6 +22,7 @@ import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
import io.netty.util.internal.AppendableCharSequence;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -111,7 +112,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
private long chunkSize;
|
||||
private int headerSize;
|
||||
private int contentRead;
|
||||
private final StringBuilder sb = new StringBuilder(128);
|
||||
private final AppendableCharSequence sb = new AppendableCharSequence(128);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
case READ_CHUNK_SIZE: try {
|
||||
StringBuilder line = readLine(buffer, maxInitialLineLength);
|
||||
AppendableCharSequence line = readLine(buffer, maxInitialLineLength);
|
||||
int chunkSize = getChunkSize(line.toString());
|
||||
this.chunkSize = chunkSize;
|
||||
if (chunkSize == 0) {
|
||||
@ -580,7 +581,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
final HttpMessage message = this.message;
|
||||
final HttpHeaders headers = message.headers();
|
||||
|
||||
StringBuilder line = readHeader(buffer);
|
||||
AppendableCharSequence line = readHeader(buffer);
|
||||
String name = null;
|
||||
String value = null;
|
||||
if (line.length() > 0) {
|
||||
@ -624,7 +625,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
|
||||
private LastHttpContent readTrailingHeaders(ByteBuf buffer) {
|
||||
headerSize = 0;
|
||||
StringBuilder line = readHeader(buffer);
|
||||
AppendableCharSequence line = readHeader(buffer);
|
||||
String lastHeader = null;
|
||||
if (line.length() > 0) {
|
||||
LastHttpContent trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);
|
||||
@ -659,9 +660,9 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
return LastHttpContent.EMPTY_LAST_CONTENT;
|
||||
}
|
||||
|
||||
private StringBuilder readHeader(ByteBuf buffer) {
|
||||
StringBuilder sb = this.sb;
|
||||
sb.setLength(0);
|
||||
private AppendableCharSequence readHeader(ByteBuf buffer) {
|
||||
AppendableCharSequence sb = this.sb;
|
||||
sb.reset();
|
||||
int headerSize = this.headerSize;
|
||||
|
||||
loop:
|
||||
@ -716,9 +717,9 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
return Integer.parseInt(hex, 16);
|
||||
}
|
||||
|
||||
private StringBuilder readLine(ByteBuf buffer, int maxLineLength) {
|
||||
StringBuilder sb = this.sb;
|
||||
sb.setLength(0);
|
||||
private AppendableCharSequence readLine(ByteBuf buffer, int maxLineLength) {
|
||||
AppendableCharSequence sb = this.sb;
|
||||
sb.reset();
|
||||
int lineLength = 0;
|
||||
while (true) {
|
||||
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 aEnd;
|
||||
int bStart;
|
||||
@ -768,7 +769,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
cStart < cEnd? sb.substring(cStart, cEnd) : "" };
|
||||
}
|
||||
|
||||
private static String[] splitHeader(StringBuilder sb) {
|
||||
private static String[] splitHeader(AppendableCharSequence sb) {
|
||||
final int length = sb.length();
|
||||
int nameStart;
|
||||
int nameEnd;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user