Skip to content

Commit

Permalink
Introduce ByteBufUtils#byteBuf2Chars
Browse files Browse the repository at this point in the history
  • Loading branch information
slandelle committed Jan 19, 2018
1 parent 60a8fe2 commit 771d4d7
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 32 deletions.
Expand Up @@ -15,58 +15,146 @@

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.CharsetUtil;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;

import static java.nio.charset.StandardCharsets.*;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.asynchttpclient.netty.util.Utf8ByteBufCharsetDecoder.*;

public final class ByteBufUtils {

private static final char[] EMPTY_CHARS = new char[0];
private static final ThreadLocal<CharBuffer> CHAR_BUFFERS = ThreadLocal.withInitial(() -> CharBuffer.allocate(1024));

private ByteBufUtils() {
}

public static boolean isUtf8OrUsAscii(Charset charset) {
public static byte[] byteBuf2Bytes(ByteBuf buf) {
int readable = buf.readableBytes();
int readerIndex = buf.readerIndex();
if (buf.hasArray()) {
byte[] array = buf.array();
if (buf.arrayOffset() == 0 && readerIndex == 0 && array.length == readable) {
return array;
}
}
byte[] array = new byte[readable];
buf.getBytes(readerIndex, array);
return array;
}

public static String byteBuf2String(Charset charset, ByteBuf buf) {
return isUtf8OrUsAscii(charset) ? decodeUtf8(buf) : buf.toString(charset);
}

public static String byteBuf2String(Charset charset, ByteBuf... bufs) {
return isUtf8OrUsAscii(charset) ? decodeUtf8(bufs) : byteBuf2String0(charset, bufs);
}

public static char[] byteBuf2Chars(Charset charset, ByteBuf buf) {
return isUtf8OrUsAscii(charset) ? decodeUtf8Chars(buf) : decodeChars(buf, charset);
}

public static char[] byteBuf2Chars(Charset charset, ByteBuf... bufs) {
return isUtf8OrUsAscii(charset) ? decodeUtf8Chars(bufs) : byteBuf2Chars0(charset, bufs);
}

private static boolean isUtf8OrUsAscii(Charset charset) {
return charset.equals(UTF_8) || charset.equals(US_ASCII);
}

public static String byteBuf2StringDefault(Charset charset, ByteBuf... bufs) {
private static char[] decodeChars(ByteBuf src, Charset charset) {
int readerIndex = src.readerIndex();
int len = src.readableBytes();

if (bufs.length == 1) {
return bufs[0].toString(charset);
if (len == 0) {
return EMPTY_CHARS;
}

for (ByteBuf buf : bufs) {
buf.retain();
final CharsetDecoder decoder = CharsetUtil.decoder(charset);
final int maxLength = (int) ((double) len * decoder.maxCharsPerByte());
CharBuffer dst = CHAR_BUFFERS.get();
if (dst.length() < maxLength) {
dst = CharBuffer.allocate(maxLength);
CHAR_BUFFERS.set(dst);
} else {
dst.clear();
}
if (src.nioBufferCount() == 1) {
// Use internalNioBuffer(...) to reduce object creation.
decode(decoder, src.internalNioBuffer(readerIndex, len), dst);
} else {
// We use a heap buffer as CharsetDecoder is most likely able to use a fast-path if src and dst buffers
// are both backed by a byte array.
ByteBuf buffer = src.alloc().heapBuffer(len);
try {
buffer.writeBytes(src, readerIndex, len);
// Use internalNioBuffer(...) to reduce object creation.
decode(decoder, buffer.internalNioBuffer(buffer.readerIndex(), len), dst);
} finally {
// Release the temporary buffer again.
buffer.release();
}
}
dst.flip();
return toCharArray(dst);
}

ByteBuf composite = Unpooled.wrappedBuffer(bufs);

static String byteBuf2String0(Charset charset, ByteBuf... bufs) {
if (bufs.length == 1) {
return bufs[0].toString(charset);
}
ByteBuf composite = composite(bufs);
try {
return composite.toString(charset);
} finally {
composite.release();
}
}

public static String byteBuf2String(Charset charset, ByteBuf buf) {
return isUtf8OrUsAscii(charset) ? Utf8ByteBufCharsetDecoder.decodeUtf8(buf) : buf.toString(charset);
static char[] byteBuf2Chars0(Charset charset, ByteBuf... bufs) {
if (bufs.length == 1) {
return decodeChars(bufs[0], charset);
}
ByteBuf composite = composite(bufs);
try {
return decodeChars(composite, charset);
} finally {
composite.release();
}
}

public static String byteBuf2String(Charset charset, ByteBuf... bufs) {
return isUtf8OrUsAscii(charset) ? Utf8ByteBufCharsetDecoder.decodeUtf8(bufs) : byteBuf2StringDefault(charset, bufs);
private static ByteBuf composite(ByteBuf[] bufs) {
for (ByteBuf buf : bufs) {
buf.retain();
}
return Unpooled.wrappedBuffer(bufs);
}

public static byte[] byteBuf2Bytes(ByteBuf buf) {
int readable = buf.readableBytes();
int readerIndex = buf.readerIndex();
if (buf.hasArray()) {
byte[] array = buf.array();
if (buf.arrayOffset() == 0 && readerIndex == 0 && array.length == readable) {
return array;
private static void decode(CharsetDecoder decoder, ByteBuffer src, CharBuffer dst) {
try {
CoderResult cr = decoder.decode(src, dst, true);
if (!cr.isUnderflow()) {
cr.throwException();
}
cr = decoder.flush(dst);
if (!cr.isUnderflow()) {
cr.throwException();
}
} catch (CharacterCodingException x) {
throw new IllegalStateException(x);
}
byte[] array = new byte[readable];
buf.getBytes(readerIndex, array);
return array;
}

static char[] toCharArray(CharBuffer charBuffer) {
char[] chars = new char[charBuffer.remaining()];
charBuffer.get(chars);
return chars;
}
}
Expand Up @@ -22,6 +22,7 @@
import java.nio.charset.CodingErrorAction;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.asynchttpclient.netty.util.ByteBufUtils.*;

public class Utf8ByteBufCharsetDecoder {

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

inspectByteBufs(bufs);
if (withoutArray) {
return ByteBufUtils.byteBuf2StringDefault(UTF_8, bufs);
return ByteBufUtils.byteBuf2String0(UTF_8, bufs);
} else {
decodeHeap0(bufs);
return charBuffer.toString();
Expand All @@ -223,7 +224,7 @@ public char[] decodeChars(ByteBuf... bufs) {

inspectByteBufs(bufs);
if (withoutArray) {
return ByteBufUtils.byteBuf2StringDefault(UTF_8, bufs).toCharArray();
return ByteBufUtils.byteBuf2Chars0(UTF_8, bufs);
} else {
decodeHeap0(bufs);
return toCharArray(charBuffer);
Expand Down Expand Up @@ -255,12 +256,6 @@ private void decodeHeap0(ByteBuf[] bufs) {
charBuffer.flip();
}

private static char[] toCharArray(CharBuffer charBuffer) {
char[] chars = new char[charBuffer.remaining()];
charBuffer.get(chars);
return chars;
}

private void inspectByteBufs(ByteBuf[] bufs) {
for (ByteBuf buf : bufs) {
if (!buf.hasArray()) {
Expand Down

0 comments on commit 771d4d7

Please sign in to comment.