org.asynchttpclient.shaded.netty.util.ByteBufUtils Maven / Gradle / Ivy
/*
* Copyright (c) 2015 AsyncHttpClient Project. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package org.asynchttpclient.shaded.netty.util;
import org.asynchttpclient.shaded.io.netty.buffer.ByteBuf;
import org.asynchttpclient.shaded.io.netty.buffer.Unpooled;
import org.asynchttpclient.shaded.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.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.asynchttpclient.shaded.netty.util.Utf8ByteBufCharsetDecoder.*;
public final class ByteBufUtils {
private static final char[] EMPTY_CHARS = new char[0];
private static final ThreadLocal CHAR_BUFFERS = ThreadLocal.withInitial(() -> CharBuffer.allocate(1024));
private ByteBufUtils() {
}
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);
}
private static char[] decodeChars(ByteBuf src, Charset charset) {
int readerIndex = src.readerIndex();
int len = src.readableBytes();
if (len == 0) {
return EMPTY_CHARS;
}
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);
}
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();
}
}
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();
}
}
private static ByteBuf composite(ByteBuf[] bufs) {
for (ByteBuf buf : bufs) {
buf.retain();
}
return Unpooled.wrappedBuffer(bufs);
}
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);
}
}
static char[] toCharArray(CharBuffer charBuffer) {
char[] chars = new char[charBuffer.remaining()];
charBuffer.get(chars);
return chars;
}
}