Skip to content

Commit 771d4d7

Browse files
committed
Introduce ByteBufUtils#byteBuf2Chars
1 parent 60a8fe2 commit 771d4d7

File tree

2 files changed

+115
-32
lines changed

2 files changed

+115
-32
lines changed

netty-utils/src/main/java/org/asynchttpclient/netty/util/ByteBufUtils.java

Lines changed: 112 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,58 +15,146 @@
1515

1616
import io.netty.buffer.ByteBuf;
1717
import io.netty.buffer.Unpooled;
18+
import io.netty.util.CharsetUtil;
1819

20+
import java.nio.ByteBuffer;
21+
import java.nio.CharBuffer;
22+
import java.nio.charset.CharacterCodingException;
1923
import java.nio.charset.Charset;
24+
import java.nio.charset.CharsetDecoder;
25+
import java.nio.charset.CoderResult;
2026

21-
import static java.nio.charset.StandardCharsets.*;
27+
import static java.nio.charset.StandardCharsets.US_ASCII;
28+
import static java.nio.charset.StandardCharsets.UTF_8;
29+
import static org.asynchttpclient.netty.util.Utf8ByteBufCharsetDecoder.*;
2230

2331
public final class ByteBufUtils {
2432

33+
private static final char[] EMPTY_CHARS = new char[0];
34+
private static final ThreadLocal<CharBuffer> CHAR_BUFFERS = ThreadLocal.withInitial(() -> CharBuffer.allocate(1024));
35+
2536
private ByteBufUtils() {
2637
}
2738

28-
public static boolean isUtf8OrUsAscii(Charset charset) {
39+
public static byte[] byteBuf2Bytes(ByteBuf buf) {
40+
int readable = buf.readableBytes();
41+
int readerIndex = buf.readerIndex();
42+
if (buf.hasArray()) {
43+
byte[] array = buf.array();
44+
if (buf.arrayOffset() == 0 && readerIndex == 0 && array.length == readable) {
45+
return array;
46+
}
47+
}
48+
byte[] array = new byte[readable];
49+
buf.getBytes(readerIndex, array);
50+
return array;
51+
}
52+
53+
public static String byteBuf2String(Charset charset, ByteBuf buf) {
54+
return isUtf8OrUsAscii(charset) ? decodeUtf8(buf) : buf.toString(charset);
55+
}
56+
57+
public static String byteBuf2String(Charset charset, ByteBuf... bufs) {
58+
return isUtf8OrUsAscii(charset) ? decodeUtf8(bufs) : byteBuf2String0(charset, bufs);
59+
}
60+
61+
public static char[] byteBuf2Chars(Charset charset, ByteBuf buf) {
62+
return isUtf8OrUsAscii(charset) ? decodeUtf8Chars(buf) : decodeChars(buf, charset);
63+
}
64+
65+
public static char[] byteBuf2Chars(Charset charset, ByteBuf... bufs) {
66+
return isUtf8OrUsAscii(charset) ? decodeUtf8Chars(bufs) : byteBuf2Chars0(charset, bufs);
67+
}
68+
69+
private static boolean isUtf8OrUsAscii(Charset charset) {
2970
return charset.equals(UTF_8) || charset.equals(US_ASCII);
3071
}
3172

32-
public static String byteBuf2StringDefault(Charset charset, ByteBuf... bufs) {
73+
private static char[] decodeChars(ByteBuf src, Charset charset) {
74+
int readerIndex = src.readerIndex();
75+
int len = src.readableBytes();
3376

34-
if (bufs.length == 1) {
35-
return bufs[0].toString(charset);
77+
if (len == 0) {
78+
return EMPTY_CHARS;
3679
}
37-
38-
for (ByteBuf buf : bufs) {
39-
buf.retain();
80+
final CharsetDecoder decoder = CharsetUtil.decoder(charset);
81+
final int maxLength = (int) ((double) len * decoder.maxCharsPerByte());
82+
CharBuffer dst = CHAR_BUFFERS.get();
83+
if (dst.length() < maxLength) {
84+
dst = CharBuffer.allocate(maxLength);
85+
CHAR_BUFFERS.set(dst);
86+
} else {
87+
dst.clear();
4088
}
89+
if (src.nioBufferCount() == 1) {
90+
// Use internalNioBuffer(...) to reduce object creation.
91+
decode(decoder, src.internalNioBuffer(readerIndex, len), dst);
92+
} else {
93+
// We use a heap buffer as CharsetDecoder is most likely able to use a fast-path if src and dst buffers
94+
// are both backed by a byte array.
95+
ByteBuf buffer = src.alloc().heapBuffer(len);
96+
try {
97+
buffer.writeBytes(src, readerIndex, len);
98+
// Use internalNioBuffer(...) to reduce object creation.
99+
decode(decoder, buffer.internalNioBuffer(buffer.readerIndex(), len), dst);
100+
} finally {
101+
// Release the temporary buffer again.
102+
buffer.release();
103+
}
104+
}
105+
dst.flip();
106+
return toCharArray(dst);
107+
}
41108

42-
ByteBuf composite = Unpooled.wrappedBuffer(bufs);
43-
109+
static String byteBuf2String0(Charset charset, ByteBuf... bufs) {
110+
if (bufs.length == 1) {
111+
return bufs[0].toString(charset);
112+
}
113+
ByteBuf composite = composite(bufs);
44114
try {
45115
return composite.toString(charset);
46116
} finally {
47117
composite.release();
48118
}
49119
}
50120

51-
public static String byteBuf2String(Charset charset, ByteBuf buf) {
52-
return isUtf8OrUsAscii(charset) ? Utf8ByteBufCharsetDecoder.decodeUtf8(buf) : buf.toString(charset);
121+
static char[] byteBuf2Chars0(Charset charset, ByteBuf... bufs) {
122+
if (bufs.length == 1) {
123+
return decodeChars(bufs[0], charset);
124+
}
125+
ByteBuf composite = composite(bufs);
126+
try {
127+
return decodeChars(composite, charset);
128+
} finally {
129+
composite.release();
130+
}
53131
}
54132

55-
public static String byteBuf2String(Charset charset, ByteBuf... bufs) {
56-
return isUtf8OrUsAscii(charset) ? Utf8ByteBufCharsetDecoder.decodeUtf8(bufs) : byteBuf2StringDefault(charset, bufs);
133+
private static ByteBuf composite(ByteBuf[] bufs) {
134+
for (ByteBuf buf : bufs) {
135+
buf.retain();
136+
}
137+
return Unpooled.wrappedBuffer(bufs);
57138
}
58139

59-
public static byte[] byteBuf2Bytes(ByteBuf buf) {
60-
int readable = buf.readableBytes();
61-
int readerIndex = buf.readerIndex();
62-
if (buf.hasArray()) {
63-
byte[] array = buf.array();
64-
if (buf.arrayOffset() == 0 && readerIndex == 0 && array.length == readable) {
65-
return array;
140+
private static void decode(CharsetDecoder decoder, ByteBuffer src, CharBuffer dst) {
141+
try {
142+
CoderResult cr = decoder.decode(src, dst, true);
143+
if (!cr.isUnderflow()) {
144+
cr.throwException();
145+
}
146+
cr = decoder.flush(dst);
147+
if (!cr.isUnderflow()) {
148+
cr.throwException();
66149
}
150+
} catch (CharacterCodingException x) {
151+
throw new IllegalStateException(x);
67152
}
68-
byte[] array = new byte[readable];
69-
buf.getBytes(readerIndex, array);
70-
return array;
153+
}
154+
155+
static char[] toCharArray(CharBuffer charBuffer) {
156+
char[] chars = new char[charBuffer.remaining()];
157+
charBuffer.get(chars);
158+
return chars;
71159
}
72160
}

netty-utils/src/main/java/org/asynchttpclient/netty/util/Utf8ByteBufCharsetDecoder.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.nio.charset.CodingErrorAction;
2323

2424
import static java.nio.charset.StandardCharsets.UTF_8;
25+
import static org.asynchttpclient.netty.util.ByteBufUtils.*;
2526

2627
public class Utf8ByteBufCharsetDecoder {
2728

@@ -209,7 +210,7 @@ public String decode(ByteBuf... bufs) {
209210

210211
inspectByteBufs(bufs);
211212
if (withoutArray) {
212-
return ByteBufUtils.byteBuf2StringDefault(UTF_8, bufs);
213+
return ByteBufUtils.byteBuf2String0(UTF_8, bufs);
213214
} else {
214215
decodeHeap0(bufs);
215216
return charBuffer.toString();
@@ -223,7 +224,7 @@ public char[] decodeChars(ByteBuf... bufs) {
223224

224225
inspectByteBufs(bufs);
225226
if (withoutArray) {
226-
return ByteBufUtils.byteBuf2StringDefault(UTF_8, bufs).toCharArray();
227+
return ByteBufUtils.byteBuf2Chars0(UTF_8, bufs);
227228
} else {
228229
decodeHeap0(bufs);
229230
return toCharArray(charBuffer);
@@ -255,12 +256,6 @@ private void decodeHeap0(ByteBuf[] bufs) {
255256
charBuffer.flip();
256257
}
257258

258-
private static char[] toCharArray(CharBuffer charBuffer) {
259-
char[] chars = new char[charBuffer.remaining()];
260-
charBuffer.get(chars);
261-
return chars;
262-
}
263-
264259
private void inspectByteBufs(ByteBuf[] bufs) {
265260
for (ByteBuf buf : bufs) {
266261
if (!buf.hasArray()) {

0 commit comments

Comments
 (0)