Netty的前置学习
文章目录
NIO
什么是NIO
New/NoneBlocking IO的简写。
NIO是一个可以替代标准java IO API的IO API(JKD1.4),在IO基础之上进行改进,最大改进在NIO有一个非阻塞的IO,面向缓冲区的,原来的IO是阻塞的,面向流的。
NIO效率比IO高,非阻塞IO常用于网络通信
在IO中程序读取文件用InputStream输入流,写文件用OutputStream输出流,是单向的
NIO中保留了IO本质的读写操作,文件和程序之间存在有通道+缓冲区,使用缓冲区进行双向的读写操作,缓冲区可以复用,通道可以比作成马路,缓冲区比作成一辆货车,在文件和程序两端来回传输数据。
缓冲区
什么是缓冲区?
在传输数据过程做存放数据的作用,和通道一起配合使用
存储数据有很多类型:
ByteBuffer
LongBuffer
IntegerBuffer
FloatBuffer
DoubleBuffer
没有布尔类型,其中ByteBuffer使用最多,传输数据基本都是传String
ByteBuffer-HeapByteBuffer类源码解析
首先看构造函数,其实用ByteBuffer.allocate新建ByteBuffer是实例化它的子类HeapByteBuffer(读/写堆字节缓冲区,是非直接缓冲区)
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
ByteBuffer继承了Buffer类
Buffer类的属性
/**
* The characteristics of Spliterators that traverse and split elements
* maintained in Buffers.
*/
static final int SPLITERATOR_CHARACTERISTICS =
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
// Used only by direct buffers
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
long address;
// Creates a new buffer with the given mark, position, limit, and capacity,
// after checking invariants.
//
重要的4个属性mark,position,limit,capacity
最后一个address,仅由直接缓冲区使用
类型都是整数型
mark:mrak是一个索引,通过Buffer中mark()方法指定Buffer中一个特定的position,之后可以通过调用reset()恢复到这个position
position:缓冲区正在操作的位置
limit:缓冲区可用大小
capacity:缓冲区最大容量,一旦声明不能改变
核心方法:
put:往buffer中存放数据,本质上写一个数组,依次写一个值,position++
public final ByteBuffer put(byte[] src) {
return put(src, 0, src.length);
}
public ByteBuffer put(byte[] src, int offset, int length) {
checkBounds(offset, length, src.length);
if (length > remaining())
throw new BufferOverflowException();
int end = offset + length;
for (int i = offset; i < end; i++)
this.put(src[i]);
return this;
}
public ByteBuffer put(byte x) {
hb[ix(nextPutIndex())] = x;
return this;
}
final int nextPutIndex() { // package-private
if (position >= limit)
throw new BufferOverflowException();
return position++;
}
get:从buffer获取数据,本质上读一个数组,依次读一个值,position++
public ByteBuffer get(byte[] dst) {
return get(dst, 0, dst.length);
}
public ByteBuffer get(byte[] dst, int offset, int length) {
checkBounds(offset, length, dst.length);
if (length > remaining())
throw new BufferUnderflowException();
int end = offset + length;
for (int i = offset; i < end; i++)
dst[i] = get();
return this;
}
public final int remaining() {
return limit - position;
}
static void checkBounds(int off, int len, int size) { // package-private
if ((off | len | (off + len) | (size - (off + len))) < 0)
throw new IndexOutOfBoundsException();
}
public byte get() {
return hb[ix(nextGetIndex())];
}
final int nextGetIndex() { // package-private
if (position >= limit)
throw new BufferUnderflowException();
return position++;
}
flip读模式 flip(),看源码也可以明白了为什么要多一个limit属性来表示可用大小了,目的是在读时候仅返回存放数据大小的数据,并且读完后的回到初始的position
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
rewind读模式rewind(),没有设置limit,读完后的position直接到buffer末尾
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
clear(),清空缓冲区,类似软删除,并没有真正的删除,只是将position置为0,再次写入新数据覆盖旧数据。
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
ByteBuffer例子
import org.junit.Test;
import java.nio.ByteBuffer;
public class Test1 {
@Test
public void bufferTest() {
// 初始化buffer大小
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 打印正在操作的位置
System.out.println(byteBuffer.position());
// 打印缓冲区可用大小 1024
System.out.println(byteBuffer.limit());
// 打印最大容量 1024
System.out.println(byteBuffer.capacity());
System.out.println("=========================");
System.out.println("存放数据");
// 存放数据
byteBuffer.put("abcde".getBytes());
// 打印正在操作的位置
System.out.println(byteBuffer.position());
// 打印缓冲区可用大小 1024
System.out.println(byteBuffer.limit());
// 打印最大容量 1024
System.out.println(byteBuffer.capacity());
System.out.println("=========================");
System.out.println("读取数据");
// 开启读取模式,作用是将position标识为0
byteBuffer.flip();
// 打印正在操作的位置
System.out.println(byteBuffer.position());
// 创建byte[]
byte[] bytes = new byte[byteBuffer.limit()];
// 获取所有值存放到bytes中,读取完后position还原成5
byteBuffer.get(bytes);
System.out.println(new String(bytes, 0, bytes.length));
System.out.println(byteBuffer.position());
System.out.println(bytes.length);
System.out.println("=========================");
System.out.println("清空缓冲区");
byteBuffer.clear();
}
@Test
public void testRewind(){
// 初始化buffer大小
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 打印正在操作的位置
System.out.println(byteBuffer.position());
// 打印缓冲区可用大小 1024
System.out.println(byteBuffer.limit());
// 打印最大容量 1024
System.out.println(byteBuffer.capacity());
System.out.println("=========================");
System.out.println("存放数据");
// 存放数据
byteBuffer.put("abcde".getBytes());
// 打印正在操作的位置
System.out.println(byteBuffer.position());
// 打印缓冲区可用大小 1024
System.out.println(byteBuffer.limit());
// 打印最大容量 1024
System.out.println(byteBuffer.capacity());
System.out.println("=========================");
System.out.println("读取数据");
// 开启重复读模式
byteBuffer.rewind();
// 打印正在操作的位置
System.out.println(byteBuffer.position());
// 创建byte[]
byte[] bytes = new byte[byteBuffer.limit()];
// 获取所有值存放到bytes中
byteBuffer.get(bytes);
System.out.println(new String(bytes, 0, bytes.length));
System.out.println(byteBuffer.position());
System.out.println(bytes.length);
}
}
mark和reset
标记与重置:mrak是一个索引,通过Buffer中mark()方法指定Buffer中一个特定的position,之后可以通过调用reset()恢复到这个position
mark例子
@Test
public void testMark() {
// 初始化buffer大小
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
byteBuffer.put("abcd".getBytes());
// 开启读模式
byteBuffer.flip();
byte[] bytes = new byte[byteBuffer.limit()];
// 只取两个值
byteBuffer.get(bytes, 0, 2);
System.out.println(new String(bytes, 0, bytes.length));
System.out.println("当前位置:" + byteBuffer.position());
byteBuffer.mark();
byteBuffer.get(bytes, 2, 2);
System.out.println(new String(bytes, 0, bytes.length));
System.out.println("当前位置:" + byteBuffer.position());
byteBuffer.reset();
System.out.println("回到原来的位置:" + byteBuffer.position());
}
顺便看一下源码,很简单
public final Buffer mark() {
mark = position;
return this;
}
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
直接缓冲区和非直接缓冲区
非直接缓冲区主要存放在jvm缓冲区中
直接缓冲区存放在物理内存中
存放在物理内存中效率高,因为直接缓冲区不需要来回的拷贝,直接操作内存,而非直接缓冲区需要来回拷贝。
下图是应用程序使用非直接缓冲区读写数据流程,读的时候需要将物理内存的数据copy到jvm空间,写的时候需要将jvm空间数据copy到物理内存中去
ByteBuffer.allocate方法创建的缓冲区是非直接缓冲区
想创建直接缓冲区调用ByteBuffer.allocateDirect,底层使用的是DirectByteBuffer。
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
DirectByteBuffer(int cap) { // package-private
super(-1, 0, cap, cap);
boolean pa = VM.isDirectMemoryPageAligned();
int ps = Bits.pageSize();
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
Bits.reserveMemory(size, cap);
long base = 0;
try {
base = unsafe.allocateMemory(size);
} catch (OutOfMemoryError x) {
Bits.unreserveMemory(size, cap);
throw x;
}
unsafe.setMemory(base, size, (byte) 0);
if (pa && (base % ps != 0)) {
// Round up to page boundary
address = base + ps - (base & (ps - 1));
} else {
address = base;
}
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
att = null;
}
DirectByteBuffer类使用Unsafe类进行操作了,直接到底层地狱,就不解析了
public ByteBuffer put(int i, byte x) {
unsafe.putByte(ix(checkIndex(i)), ((x)));
return this;
}
甚至都不是java实现的了
所以跳过吧
哪一种缓冲区更为安全?
非直接缓冲区,为什么呢?
因为直接内存直接读写物理内存,不安全,出现异常时可能造成数据只写了一半,或者在写的时候,其他的程序可能会清除或修改了之前写的内容。
简易病毒制作
使用DirectByteBuffer,用死循环一直往内存里写东西,电脑会卡到只能重启。
IO 缓冲区
原先的IO缓冲区使用的非直接缓冲区
通道
什么是NIO通道?
表示打开IO设备(例如:文件,socket)的连接 。获取IO设备的通道后,程序可以操作缓冲区,对数据进行处理。Channel赋值传输,Buffer负责存储。
Channel类似传统的Stream,但是自身不能访问数据,要与buffer配合使用
channels包下有File、Socket、ServerSocket、DatagramChannel等抽象类
他们的实现在_____ChannelImpl
jdk1.7后新增了open方法创建channel
Channel例子
非直接缓冲区
@SneakyThrows
@Test
public void testChannel() {
long startTime = System.currentTimeMillis();
// 读入流
FileInputStream fi = new FileInputStream("t.txt");
// 写入流
FileOutputStream fo = new FileOutputStream("2.txt");
// 创建通道
FileChannel inChannel = fi.getChannel();
FileChannel outChannel = fo.getChannel();
// 分配指定大小缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 读写操作
while (inChannel.read(buffer) != -1) {
// 开启读取模式
buffer.flip();
// 写入到通道中
outChannel.write(buffer);
// 读完后清空缓冲区
buffer.clear();
}
// 关闭通道
inChannel.close();
outChannel.close();
fi.close();
fo.close();
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
直接缓冲区
@SneakyThrows
@Test
public void directChannel() {
long startTime = System.currentTimeMillis();
// 创建管道
FileChannel in = FileChannel.open(Paths.get("t.txt"), StandardOpenOption.READ);
FileChannel out = FileChannel.open(Paths.get("2.txt"),
StandardOpenOption.READ,
StandardOpenOption.WRITE,
StandardOpenOption.CREATE);
// 定义映射文件
MappedByteBuffer inMap = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
MappedByteBuffer outMap = out.map(FileChannel.MapMode.READ_WRITE, 0, in.size());
// 直接对缓冲区操作
byte[] bytes = new byte[inMap.limit()];
// 读取
inMap.get(bytes);
// 写入
outMap.put(bytes);
// 关闭管道
in.close();
out.close();
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
分散读取和聚集写入
什么是分散读取和聚集写入
一条通道支持多个buffer,多个buffer可以整合成一个buffer
分散读取:将通道中的数据分散到多个缓冲区中
聚集写入:将多个缓冲区的数据聚集通道中
多个buffer同时读,提高效率,最后聚集起来写入文件
例子
@SneakyThrows
@Test
public void disperseAndGather(){
// SE 随机访问
RandomAccessFile raf = new RandomAccessFile("t.txt", "rw");
// 获取通道t
FileChannel channel = raf.getChannel();
// 分配指定缓冲区
ByteBuffer buffer1 = ByteBuffer.allocate(100);
ByteBuffer buffer2 = ByteBuffer.allocate(1024);
// 分散读取
ByteBuffer[] buffers = {buffer1, buffer2};
channel.read(buffers);
System.out.println(new String(buffers[0].array(), 0, buffers[0].limit()));
System.out.println(new String(buffers[1].array(), 0, buffers[1].limit()));
System.out.println("--分散读取完毕--");
for (ByteBuffer b: buffers){
// 切换读模式
b.flip();
}
// 聚集读写
RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");
// 获取通道
FileChannel channel2 = raf2.getChannel();
// 写入
channel2.write(buffers);
}
分析源码,drl
IO包中的getChannel源码,我们可以看到其实底层是调用的Channel的open方法
public final FileChannel getChannel() {
synchronized (this) {
if (channel == null) {
channel = FileChannelImpl.open(fd, path, true, rw, this);
}
return channel;
}
}
反编译出来的open方法,返回的对象是FileChannelImpl
public static FileChannel open(FileDescriptor var0, String var1, boolean var2, boolean var3, Object var4) {
return new FileChannelImpl(var0, var1, var2, var3, false, var4);
}
分散读底层实现,用了synchronized同步代码块来保证的线程安全,不过编译出来的可读性太差了,勉强能看出用了IOUtil循环读数据,有他们nio自定义的Thread类,还用到了IOStatus,以后再研究一下吧,这篇先加入收藏夹了https://www.cnblogs.com/Jack-Blog/p/12078767.html。收藏从未停下,学习何时开始
// 抽象类中的分散读
public final long read(ByteBuffer[] dsts) throws IOException {
return read(dsts, 0, dsts.length);
}
public abstract long read(ByteBuffer[] dsts, int offset, int length) throws IOException;
// 分散读实现
public long read(ByteBuffer[] var1, int var2, int var3) throws IOException {
if (var2 >= 0 && var3 >= 0 && var2 <= var1.length - var3) {
this.ensureOpen();
if (!this.readable) {
throw new NonReadableChannelException();
} else {
synchronized(this.positionLock) {
long var5 = 0L;
int var7 = -1;
try {
this.begin();
var7 = this.threads.add();
long var8;
if (!this.isOpen()) {
var8 = 0L;
return var8;
} else {
do {
var5 = IOUtil.read(this.fd, var1, var2, var3, this.nd);
} while(var5 == -3L && this.isOpen());
var8 = IOStatus.normalize(var5);
return var8;
}
} finally {
this.threads.remove(var7);
this.end(var5 > 0L);
assert IOStatus.check(var5);
}
}
}
} else {
throw new IndexOutOfBoundsException();
}
}
编码格式
了解即可
Charset,CharsetEncoder和CharsetDecoder进行加密解密
例子说明
@SneakyThrows
@Test
public void charsetTest(){
// 获取编码器
Charset charset = Charset.forName("UTF-8");
// 加密器
CharsetEncoder ce = charset.newEncoder();
// 获取解码器
CharsetDecoder cd = charset.newDecoder();
// 创建缓冲区
CharBuffer buffer = CharBuffer.allocate(1024);
buffer.put("测试122345677");
// 读模式,将position置0
buffer.flip();
// 编码格式
ByteBuffer encodeBuf = ce.encode(buffer);
for (int i = 0; i < encodeBuf.limit(); i++) {
// 输出加密的内容
System.out.println(encodeBuf.get());
}
// 读模式,position置0
encodeBuf.flip();
// 将编码解密
CharBuffer decodeBuf = cd.decode(encodeBuf);
System.out.println(decodeBuf.toString());
}
encode源码
将单个输入字符缓冲区的剩余内容编码到一个新分配的字节缓冲区中。(*UP注释,因此使用前一般都用flip()新置一下position和limit)
这个方法实现了一个完整的编码操作;也就是说,它先重置这个编码器,然后对给定字符缓冲区中的字符进行编码,最后刷新这个编码器。
如果一个编码操作已经在进行中,就不应该调用这个方法。
public final ByteBuffer encode(CharBuffer in)
throws CharacterCodingException
{
int n = (int)(in.remaining() * averageBytesPerChar());
ByteBuffer out = ByteBuffer.allocate(n);
if ((n == 0) && (in.remaining() == 0))
return out;
reset();
for (;;) {
CoderResult cr = in.hasRemaining() ?
encode(in, out, true) : CoderResult.UNDERFLOW;
if (cr.isUnderflow())
cr = flush(out);
if (cr.isUnderflow())
break;
if (cr.isOverflow()) {
n = 2*n + 1; // Ensure progress; n might be 0!
ByteBuffer o = ByteBuffer.allocate(n);
out.flip();
o.put(out);
out = o;
continue;
}
cr.throwException();
}
out.flip();
return out;
}
了解即可。
从给定的输入缓冲区编码尽可能多的字符,并将结果写入给定的输出缓冲区。
从当前位置开始读取和写入缓冲区。最多读取in.stay()字符,最多写入out.stay()字节。缓冲区的位置将被提前,以反映读取的字符和写入的字节,但它们的标记和限制不会被修改。
除了从输入缓冲区读取字符和向输出缓冲区写入字节外,该方法还返回一个CoderResult对象来描述其终止的原因。
CoderResult.UNDERFLOW表示尽可能多的输入缓冲区已经被编码。如果没有进一步的输入,那么调用者可以继续进行下一步的编码操作。否则应再次调用该方法,并输入更多的信息。
CoderResult.OVERFLOW表示输出缓冲区中没有足够的空间来编码更多的字符。应该在输出缓冲区有更多剩余字节的情况下再次调用本方法。这通常是通过排出输出缓冲区中的任何编码字节来实现的。
malformed-input的结果表示已经检测到一个畸形输入错误。畸形字符从输入缓冲区的(可能是递增的)位置开始;畸形字符的数量可以通过调用结果对象的长度方法来确定。这种情况只适用于该编码器的畸形动作为CodingErrorAction.report的情况;否则,畸形输入将被忽略或按要求替换。
不可映射字符的结果表示检测到了一个不可映射字符的错误。编码不可映射字符的字符从输入缓冲区的位置开始(可能是递增的);这些字符的数量可以通过调用结果对象的长度方法来确定。这种情况只适用于该编码器的不可映射动作是CodingErrorAction.report的情况;否则,不可映射字符将被忽略或按要求替换。
在任何情况下,如果这个方法要在同一个编码操作中重新调用,那么就应该注意保留输入缓冲区中剩余的任何字符,以便它们可以用于下一次调用。
endOfInput参数告知本方法,除了给定的输入缓冲区所包含的输入之外,调用者是否还能提供更多的输入。如果有可能提供额外的输入,那么调用者应该为这个参数传递false;如果没有可能提供进一步的输入,那么调用者应该传递true。在一次调用中传递false,而后来发现实际上没有进一步的输入,这并不是错误的,事实上也很常见。然而,至关重要的是,在一系列调用中,该方法的最后一次调用总是传递true,这样任何剩余的未编码输入都将被视为畸形输入。
该方法的工作原理是调用 encodeLoop 方法,解释其结果,处理错误条件,并在必要时重新调用它。
以上翻译自注解,所以讲了一大堆真真的是调用encodeLoop,其他都是错误处理
public final CoderResult encode(CharBuffer in, ByteBuffer out,
boolean endOfInput)
{
int newState = endOfInput ? ST_END : ST_CODING;
if ((state != ST_RESET) && (state != ST_CODING)
&& !(endOfInput && (state == ST_END)))
throwIllegalStateException(state, newState);
state = newState;
for (;;) {
CoderResult cr;
try {
cr = encodeLoop(in, out);
} catch (BufferUnderflowException x) {
throw new CoderMalfunctionError(x);
} catch (BufferOverflowException x) {
throw new CoderMalfunctionError(x);
}
if (cr.isOverflow())
return cr;
if (cr.isUnderflow()) {
if (endOfInput && in.hasRemaining()) {
cr = CoderResult.malformedForLength(in.remaining());
// Fall through to malformed-input case
} else {
return cr;
}
}
CodingErrorAction action = null;
if (cr.isMalformed())
action = malformedInputAction;
else if (cr.isUnmappable())
action = unmappableCharacterAction;
else
assert false : cr.toString();
if (action == CodingErrorAction.REPORT)
return cr;
if (action == CodingErrorAction.REPLACE) {
if (out.remaining() < replacement.length)
return CoderResult.OVERFLOW;
out.put(replacement);
}
if ((action == CodingErrorAction.IGNORE)
|| (action == CodingErrorAction.REPLACE)) {
// Skip erroneous input either way
in.position(in.position() + cr.length());
continue;
}
assert false;
}
}
encodeLoop,hasArray()方法用于确保给定缓冲区是否由可访问的字节数组支持。
protected final CoderResult encodeLoop(CharBuffer var1, ByteBuffer var2) {
return var1.hasArray() && var2.hasArray() ? this.encodeArrayLoop(var1, var2) : this.encodeBufferLoop(var1, var2);
}
这段代码实现了完整的编码操作。
(又到了底层地狱,打扰了,有谁看懂了吗,我竟然查不到encodeArrayLoop这函数的资料…)
private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst){
char[] sa = src.array();
int sp = src.arrayOffset() + src.position();
int sl = src.arrayOffset() + src.limit();
byte[] da = dst.array();
int dp = dst.arrayOffset() + dst.position();
int dl = dst.arrayOffset() + dst.limit();
int dlASCII = dp + Math.min(sl - sp, dl - dp);
// ASCII only loop
while (dp < dlASCII && sa[sp] < '\u0080')
da[dp++] = (byte) sa[sp++];
while (sp < sl) {
char c = sa[sp];
if (c < 0x80) {
// Have at most seven bits
if (dp >= dl)
return overflow(src, sp, dst, dp);
da[dp++] = (byte)c;
} else if (c < 0x800) {
// 2 bytes, 11 bits
if (dl - dp < 2)
return overflow(src, sp, dst, dp);
da[dp++] = (byte)(0xc0 | (c >> 6));
da[dp++] = (byte)(0x80 | (c & 0x3f));
} else if (Character.isSurrogate(c)) {
// Have a surrogate pair
if (sgp == null)
sgp = new Surrogate.Parser();
int uc = sgp.parse(c, sa, sp, sl);
if (uc < 0) {
updatePositions(src, sp, dst, dp);
return sgp.error();
}
if (dl - dp < 4)
return overflow(src, sp, dst, dp);
da[dp++] = (byte)(0xf0 | ((uc >> 18)));
da[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f));
da[dp++] = (byte)(0x80 | ((uc >> 6) & 0x3f));
da[dp++] = (byte)(0x80 | (uc & 0x3f));
sp++; // 2 chars
} else {
// 3 bytes, 16 bits
if (dl - dp < 3)
return overflow(src, sp, dst, dp);
da[dp++] = (byte)(0xe0 | ((c >> 12)));
da[dp++] = (byte)(0x80 | ((c >> 6) & 0x3f));
da[dp++] = (byte)(0x80 | (c & 0x3f));
}
sp++;
}
updatePositions(src, sp, dst, dp);
return CoderResult.UNDERFLOW;
}
解密源码
和加密类似,镜像的编程,就不贴出来了,感兴趣可以自己解析一下encodeArrayLoop和decodeArrayLoop,因为确实没有研究这个实现
private CoderResult decodeArrayLoop(ByteBuffer var1, CharBuffer var2) {
byte[] var3 = var1.array();
int var4 = var1.arrayOffset() + var1.position();
int var5 = var1.arrayOffset() + var1.limit();
char[] var6 = var2.array();
int var7 = var2.arrayOffset() + var2.position();
int var8 = var2.arrayOffset() + var2.limit();
for(int var9 = var7 + Math.min(var5 - var4, var8 - var7); var7 < var9 && var3[var4] >= 0; var6[var7++] = (char)var3[var4++]) {
}
while(true) {
while(var4 < var5) {
byte var10 = var3[var4];
if (var10 < 0) {
if (var10 >> 5 == -2 && (var10 & 30) != 0) {
if (var5 - var4 < 2 || var7 >= var8) {
return xflow(var1, var4, var5, var2, var7, 2);
}
byte var17 = var3[var4 + 1];
if (isNotContinuation(var17)) {
return malformedForLength(var1, var4, var2, var7, 1);
}
var6[var7++] = (char)(var10 << 6 ^ var17 ^ 3968);
var4 += 2;
} else {
int var11;
byte var12;
byte var13;
if (var10 >> 4 == -2) {
var11 = var5 - var4;
if (var11 < 3 || var7 >= var8) {
if (var11 > 1 && isMalformed3_2(var10, var3[var4 + 1])) {
return malformedForLength(var1, var4, var2, var7, 1);
}
return xflow(var1, var4, var5, var2, var7, 3);
}
var12 = var3[var4 + 1];
var13 = var3[var4 + 2];
if (isMalformed3(var10, var12, var13)) {
return malformed(var1, var4, var2, var7, 3);
}
char var18 = (char)(var10 << 12 ^ var12 << 6 ^ var13 ^ -123008);
if (Character.isSurrogate(var18)) {
return malformedForLength(var1, var4, var2, var7, 3);
}
var6[var7++] = var18;
var4 += 3;
} else {
if (var10 >> 3 != -2) {
return malformed(var1, var4, var2, var7, 1);
}
var11 = var5 - var4;
if (var11 >= 4 && var8 - var7 >= 2) {
var12 = var3[var4 + 1];
var13 = var3[var4 + 2];
byte var14 = var3[var4 + 3];
int var15 = var10 << 18 ^ var12 << 12 ^ var13 << 6 ^ var14 ^ 3678080;
if (!isMalformed4(var12, var13, var14) && Character.isSupplementaryCodePoint(var15)) {
var6[var7++] = Character.highSurrogate(var15);
var6[var7++] = Character.lowSurrogate(var15);
var4 += 4;
continue;
}
return malformed(var1, var4, var2, var7, 4);
}
int var16 = var10 & 255;
if (var16 <= 244 && (var11 <= 1 || !isMalformed4_2(var16, var3[var4 + 1] & 255))) {
if (var11 > 2 && isMalformed4_3(var3[var4 + 2])) {
return malformedForLength(var1, var4, var2, var7, 2);
}
return xflow(var1, var4, var5, var2, var7, 4);
}
return malformedForLength(var1, var4, var2, var7, 1);
}
}
} else {
if (var7 >= var8) {
return xflow(var1, var4, var5, var2, var7, 1);
}
var6[var7++] = (char)var10;
++var4;
}
}
return xflow(var1, var4, var5, var2, var7, 0);
}
}