Replace usage of StringBuilder by AppendableCharSequence for performance reasons
This commit is contained in:
parent
f9a77b3c83
commit
ec8967f9ff
@ -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;
|
||||||
|
@ -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