Skip to content

Commit c555e45

Browse files
committed
Initial cut of a way to fast read ByteBufs
1 parent 6fecc4b commit c555e45

File tree

2 files changed

+178
-11
lines changed

2 files changed

+178
-11
lines changed

client/src/main/java/org/asynchttpclient/netty/util/ByteBufUtils.java

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
/*
2-
* Copyright 2010 Ning, Inc.
2+
* Copyright (c) 2015 AsyncHttpClient Project. All rights reserved.
33
*
4-
* Ning licenses this file to you under the Apache License, version 2.0
5-
* (the "License"); you may not use this file except in compliance with the
6-
* License. You may obtain a copy of the License at:
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at
7+
* http://www.apache.org/licenses/LICENSE-2.0.
78
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12-
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13-
* License for the specific language governing permissions and limitations
14-
* under the License.
9+
* Unless required by applicable law or agreed to in writing,
10+
* software distributed under the Apache License Version 2.0 is distributed on an
11+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
1513
*/
1614
package org.asynchttpclient.netty.util;
1715

1816
import io.netty.buffer.ByteBuf;
1917

18+
import java.io.UTFDataFormatException;
19+
import java.nio.ByteBuffer;
20+
import java.nio.CharBuffer;
21+
import java.nio.charset.CharacterCodingException;
22+
import java.nio.charset.Charset;
23+
import java.nio.charset.CharsetDecoder;
24+
import java.nio.charset.CoderResult;
25+
import java.nio.charset.StandardCharsets;
2026
import java.util.List;
2127

2228
public final class ByteBufUtils {
@@ -26,6 +32,53 @@ public final class ByteBufUtils {
2632
private ByteBufUtils() {
2733
}
2834

35+
public static String byteBuf2String(ByteBuf buf, Charset charset) throws UTFDataFormatException, IndexOutOfBoundsException, CharacterCodingException {
36+
37+
int byteLen = buf.readableBytes();
38+
39+
if (charset.equals(StandardCharsets.US_ASCII)) {
40+
return Utf8Reader.readUtf8(buf, byteLen);
41+
} else if (charset.equals(StandardCharsets.UTF_8)) {
42+
try {
43+
return Utf8Reader.readUtf8(buf.duplicate(), (int) (byteLen * 1.4));
44+
} catch (IndexOutOfBoundsException e) {
45+
// try again with 3 bytes per char
46+
return Utf8Reader.readUtf8(buf, byteLen * 3);
47+
}
48+
} else {
49+
return byteBuffersToString(buf.nioBuffers(), charset);
50+
}
51+
}
52+
53+
private static String byteBuffersToString(ByteBuffer[] bufs, Charset cs) throws CharacterCodingException {
54+
55+
CharsetDecoder cd = cs.newDecoder();
56+
int len = 0;
57+
for (ByteBuffer buf : bufs) {
58+
len += buf.remaining();
59+
}
60+
int en = (int) (len * (double) cd.maxCharsPerByte());
61+
char[] ca = new char[en];
62+
cd.reset();
63+
CharBuffer cb = CharBuffer.wrap(ca);
64+
65+
CoderResult cr = null;
66+
67+
for (int i = 0; i < bufs.length; i++) {
68+
69+
ByteBuffer buf = bufs[i];
70+
cr = cd.decode(buf, cb, i < bufs.length - 1);
71+
if (!cr.isUnderflow())
72+
cr.throwException();
73+
}
74+
75+
cr = cd.flush(cb);
76+
if (!cr.isUnderflow())
77+
cr.throwException();
78+
79+
return new String(ca, 0, cb.position());
80+
}
81+
2982
public static byte[] byteBuf2Bytes(ByteBuf buf) {
3083
int readable = buf.readableBytes();
3184
int readerIndex = buf.readerIndex();
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright (c) 2015 AsyncHttpClient Project. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at
7+
* http://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* Unless required by applicable law or agreed to in writing,
10+
* software distributed under the Apache License Version 2.0 is distributed on an
11+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
13+
*/
14+
package org.asynchttpclient.netty.util;
15+
16+
import io.netty.buffer.AbstractByteBuf;
17+
import io.netty.buffer.ByteBuf;
18+
import io.netty.util.concurrent.FastThreadLocal;
19+
20+
import java.io.UTFDataFormatException;
21+
22+
public class Utf8Reader {
23+
24+
private static int SMALL_BUFFER_SIZE = 4096;
25+
private static final IndexOutOfBoundsException STRING_DECODER_INDEX_OUT_OF_BOUNDS_EXCEPTION = new IndexOutOfBoundsException("String decoder index out of bounds");
26+
27+
private static final FastThreadLocal<char[]> CACHED_CHAR_BUFFERS = new FastThreadLocal<char[]>() {
28+
@Override
29+
protected char[] initialValue() throws Exception {
30+
return new char[SMALL_BUFFER_SIZE];
31+
}
32+
};
33+
34+
public static String readUtf8(ByteBuf buf, int utflen) throws UTFDataFormatException, IndexOutOfBoundsException {
35+
36+
boolean small = utflen <= SMALL_BUFFER_SIZE;
37+
char[] chararr = small ? CACHED_CHAR_BUFFERS.get() : new char[utflen];
38+
39+
int char1, char2, char3;
40+
int count = 0, chararr_count = 0;
41+
42+
if (buf.readableBytes() > utflen) {
43+
throw STRING_DECODER_INDEX_OUT_OF_BOUNDS_EXCEPTION;
44+
}
45+
46+
if (buf instanceof AbstractByteBuf) {
47+
AbstractByteBuf b = (AbstractByteBuf) buf;
48+
int readerIndex = buf.readerIndex();
49+
50+
// fast-path
51+
while (count < utflen) {
52+
char1 = b.getByte(readerIndex + count) & 0xff;
53+
if (char1 > 127)
54+
break;
55+
count++;
56+
chararr[chararr_count++] = (char) char1;
57+
}
58+
59+
while (count < utflen) {
60+
char1 = b.getByte(readerIndex + count) & 0xff;
61+
switch (char1 >> 4) {
62+
case 0:
63+
case 1:
64+
case 2:
65+
case 3:
66+
case 4:
67+
case 5:
68+
case 6:
69+
case 7:
70+
/* 0xxxxxxx */
71+
count++;
72+
chararr[chararr_count++] = (char) char1;
73+
break;
74+
case 12:
75+
case 13:
76+
/* 110x xxxx 10xx xxxx */
77+
count += 2;
78+
if (count > utflen)
79+
throw new UTFDataFormatException("malformed input: partial character at end");
80+
char2 = b.getByte(readerIndex + count - 1);
81+
if ((char2 & 0xC0) != 0x80)
82+
throw new UTFDataFormatException("malformed input around byte " + count);
83+
chararr[chararr_count++] = (char) (((char1 & 0x1F) << 6) | (char2 & 0x3F));
84+
break;
85+
case 14:
86+
/* 1110 xxxx 10xx xxxx 10xx xxxx */
87+
count += 3;
88+
if (count > utflen)
89+
throw new UTFDataFormatException("malformed input: partial character at end");
90+
char2 = b.getByte(readerIndex + count - 2);
91+
char3 = b.getByte(readerIndex + count - 1);
92+
if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
93+
throw new UTFDataFormatException("malformed input around byte " + (count - 1));
94+
chararr[chararr_count++] = (char) (((char1 & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
95+
break;
96+
default:
97+
/* 10xx xxxx, 1111 xxxx */
98+
throw new UTFDataFormatException("malformed input around byte " + count);
99+
}
100+
}
101+
102+
buf.readerIndex(buf.readerIndex() + count);
103+
104+
// The number of chars produced may be less than utflen
105+
return new String(chararr, 0, chararr_count);
106+
107+
} else {
108+
byte[] b = new byte[utflen];
109+
buf.readBytes(b);
110+
111+
return new String(b);
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)