1462 lines
49 KiB
Markdown
1462 lines
49 KiB
Markdown
# 精尽 Netty 源码解析 —— Buffer 之 ByteBuf(一)简介
|
||
|
||
# 1. 概述
|
||
|
||
从本文开始,我们来分享 Netty ByteBuf 相关的内容。它在 `buffer` 模块中实现,在功能定位上,它和 NIO ByteBuffer 是一致的,但是强大非常多。如下是 [《Netty 实战》](https://svip.iocoder.cn/Netty/ByteBuf-1-1-ByteBuf-intro/#) 对它的**优点总**结:
|
||
|
||
> - A01. 它可以被用户自定义的**缓冲区类型**扩展
|
||
> - A02. 通过内置的符合缓冲区类型实现了透明的**零拷贝**
|
||
> - A03. 容量可以**按需增长**
|
||
> - A04. 在读和写这两种模式之间切换不需要调用 `#flip()` 方法
|
||
> - A05. 读和写使用了**不同的索引**
|
||
> - A06. 支持方法的**链式**调用
|
||
> - A07. 支持引用计数
|
||
> - A08. 支持**池化**
|
||
|
||
- 特别是第 A04 这点,相信很多胖友都被 NIO ByteBuffer 反人类的读模式和写模式给坑哭了。在 [《精尽 Netty 源码分析 —— NIO 基础(三)之 Buffer》](http://svip.iocoder.cn/Netty/nio-3-buffer/) 中,我们也吐槽过了。😈
|
||
- 当然,可能胖友看着这些优点,会一脸懵逼,不要紧,边读源码边理解落。
|
||
|
||
------
|
||
|
||
> 老艿艿,从下文开始,Netty ByteBuf ,我们只打 ByteBuf 。相比 NIO ByteBuffer ,它少 `"fer"` 三个字母。
|
||
|
||
ByteBuf 的代码实现挺有趣的,但是会略有一点点深度,所以笔者会分成三大块来分享:
|
||
|
||
- ByteBuf 相关,主要是它的核心 API 和核心子类实现。
|
||
- ByteBufAllocator 相关,用于创建 ByteBuf 对象。
|
||
- Jemalloc 相关,内存管理算法,Netty 基于该算法,实现对内存高效和有效的管理。😈 这块是最最最有趣的。
|
||
|
||
每一块,我们会分成几篇小的文章。而本文,我们就来对 ByteBuf 有个整体的认识,特别是核心 API 部分。
|
||
|
||
# 2. ByteBuf
|
||
|
||
`io.netty.buffer.ByteBuf` ,实现 ReferenceCounted 、Comparable 接口,ByteBuf **抽象类**。注意,ByteBuf 是一个抽象类,而不是一个接口。当然,实际上,它主要定义了**抽象**方法,**很少**实现对应的方法。
|
||
|
||
关于 `io.netty.util.ReferenceCounted` 接口,对象引用计数器接口。
|
||
|
||
- 对象的初始引用计数为 1 。
|
||
- 当引用计数器值为 0 时,表示该对象不能再被继续引用,只能被释放。
|
||
- 本文暂时不解析,我们会在 TODO 1011
|
||
|
||
## 2.1 抽象方法
|
||
|
||
因为 ByteBuf 的方法非常多,所以笔者对它的方法做了简单的归类。Let’s Go 。
|
||
|
||
### 2.1.1 基础信息
|
||
|
||
```
|
||
public abstract int capacity(); // 容量
|
||
public abstract ByteBuf capacity(int newCapacity);
|
||
public abstract int maxCapacity(); // 最大容量
|
||
|
||
public abstract ByteBufAllocator alloc(); // 分配器,用于创建 ByteBuf 对象。
|
||
|
||
@Deprecated
|
||
public abstract ByteOrder order(); // 字节序,即大小端。推荐阅读 http://www.ruanyifeng.com/blog/2016/11/byte-order.html
|
||
@Deprecated
|
||
public abstract ByteBuf order(ByteOrder endianness);
|
||
|
||
public abstract ByteBuf unwrap(); // 获得被包装( wrap )的 ByteBuf 对象。
|
||
|
||
public abstract boolean isDirect(); // 是否 NIO Direct Buffer
|
||
|
||
public abstract boolean isReadOnly(); // 是否为只读 Buffer
|
||
public abstract ByteBuf asReadOnly();
|
||
|
||
public abstract int readerIndex(); // 读取位置
|
||
public abstract ByteBuf readerIndex(int readerIndex);
|
||
public abstract int writerIndex(); // 写入位置
|
||
public abstract ByteBuf writerIndex(int writerIndex);
|
||
public abstract ByteBuf setIndex(int readerIndex, int writerIndex); // 设置读取和写入位置
|
||
public abstract int readableBytes(); // 剩余可读字节数
|
||
public abstract int writableBytes(); // 剩余可写字节数
|
||
public abstract int maxWritableBytes();
|
||
public abstract boolean isReadable();
|
||
public abstract boolean isReadable(int size);
|
||
public abstract boolean isWritable();
|
||
public abstract boolean isWritable(int size);
|
||
public abstract ByteBuf ensureWritable(int minWritableBytes);
|
||
public abstract int ensureWritable(int minWritableBytes, boolean force);
|
||
public abstract ByteBuf markReaderIndex(); // 标记读取位置
|
||
public abstract ByteBuf resetReaderIndex();
|
||
public abstract ByteBuf markWriterIndex(); // 标记写入位置
|
||
public abstract ByteBuf resetWriterIndex();
|
||
```
|
||
|
||
主要是如下四个属性:
|
||
|
||
- `readerIndex` ,读索引。
|
||
- `writerIndex` ,写索引。
|
||
- `capacity` ,当前容量。
|
||
- `maxCapacity` ,最大容量。当 `writerIndex` 写入超过 `capacity` 时,可自动扩容。**每次**扩容的大小,为 `capacity` 的 2 倍。当然,前提是不能超过 `maxCapacity` 大小。
|
||
|
||
所以,ByteBuf 通过 `readerIndex` 和 `writerIndex` 两个索引,解决 ByteBuffer 的读写模式的问题。
|
||
|
||
四个大小关系很简单:`readerIndex` <= `writerIndex` <= `capacity` <= `maxCapacity` 。如下图所示:[简介.assets/01.png)](http://static.iocoder.cn/images/Netty/2018_08_01/01.png)分段
|
||
|
||
- 图中一共有三段,实际是四段,省略了 `capacity` 到 `maxCapacity` 之间的一段。
|
||
- discardable bytes ,废弃段。一般情况下,可以理解成已读的部分。
|
||
- readable bytes ,可读段。可通过 `#readXXX()` 方法,顺序向下读取。
|
||
- writable bytes ,可写段。可通过 `#writeXXX()` 方法,顺序向下写入。
|
||
|
||
另外,ByteBuf 还有 `markReaderIndex` 和 `markWriterIndex` 两个属性:
|
||
|
||
- 通过对应的 `#markReaderIndex()` 和 `#markWriterIndex()` 方法,分别标记读取和写入位置。
|
||
- 通过对应的 `#resetReaderIndex()` 和 `#resetWriterIndex()` 方法,分别读取和写入位置到标记处。
|
||
|
||
### 3.1.2 读取 / 写入操作
|
||
|
||
```
|
||
// Boolean 1 字节
|
||
public abstract boolean getBoolean(int index);
|
||
public abstract ByteBuf setBoolean(int index, boolean value);
|
||
public abstract boolean readBoolean();
|
||
public abstract ByteBuf writeBoolean(boolean value);
|
||
|
||
// Byte 1 字节
|
||
public abstract byte getByte(int index);
|
||
public abstract short getUnsignedByte(int index);
|
||
public abstract ByteBuf setByte(int index, int value);
|
||
public abstract byte readByte();
|
||
public abstract short readUnsignedByte();
|
||
public abstract ByteBuf writeByte(int value);
|
||
|
||
// Short 2 字节
|
||
public abstract short getShort(int index);
|
||
public abstract short getShortLE(int index);
|
||
public abstract int getUnsignedShort(int index);
|
||
public abstract int getUnsignedShortLE(int index);
|
||
public abstract ByteBuf setShort(int index, int value);
|
||
public abstract ByteBuf setShortLE(int index, int value);
|
||
public abstract short readShort();
|
||
public abstract short readShortLE();
|
||
public abstract int readUnsignedShort();
|
||
public abstract int readUnsignedShortLE();
|
||
public abstract ByteBuf writeShort(int value);
|
||
public abstract ByteBuf writeShortLE(int value);
|
||
|
||
// 【特殊】Medium 3 字节
|
||
public abstract int getMedium(int index);
|
||
public abstract int getMediumLE(int index);
|
||
public abstract int getUnsignedMedium(int index);
|
||
public abstract int getUnsignedMediumLE(int index);
|
||
public abstract ByteBuf setMedium(int index, int value);
|
||
public abstract ByteBuf setMediumLE(int index, int value);
|
||
public abstract int readMedium();
|
||
public abstract int readMediumLE();
|
||
public abstract int readUnsignedMedium();
|
||
public abstract int readUnsignedMediumLE();
|
||
public abstract ByteBuf writeMedium(int value);
|
||
public abstract ByteBuf writeMediumLE(int value);
|
||
|
||
// Int 4 字节
|
||
public abstract int getInt(int index);
|
||
public abstract int getIntLE(int index);
|
||
public abstract long getUnsignedInt(int index);
|
||
public abstract long getUnsignedIntLE(int index);
|
||
public abstract ByteBuf setInt(int index, int value);
|
||
public abstract ByteBuf setIntLE(int index, int value);
|
||
public abstract int readInt();
|
||
public abstract int readIntLE();
|
||
public abstract long readUnsignedInt();
|
||
public abstract long readUnsignedIntLE();
|
||
public abstract ByteBuf writeInt(int value);
|
||
public abstract ByteBuf writeIntLE(int value);
|
||
|
||
// Long 8 字节
|
||
public abstract long getLong(int index);
|
||
public abstract long getLongLE(int index);
|
||
public abstract ByteBuf setLong(int index, long value);
|
||
public abstract ByteBuf setLongLE(int index, long value);
|
||
public abstract long readLong();
|
||
public abstract long readLongLE();
|
||
public abstract ByteBuf writeLong(long value);
|
||
public abstract ByteBuf writeLongLE(long value);
|
||
|
||
// Char 2 字节
|
||
public abstract char getChar(int index);
|
||
public abstract ByteBuf setChar(int index, int value);
|
||
public abstract char readChar();
|
||
public abstract ByteBuf writeChar(int value);
|
||
|
||
// Float 4 字节
|
||
public abstract float getFloat(int index);
|
||
public float getFloatLE(int index) {
|
||
return Float.intBitsToFloat(getIntLE(index));
|
||
}
|
||
public abstract ByteBuf setFloat(int index, float value);
|
||
public ByteBuf setFloatLE(int index, float value) {
|
||
return setIntLE(index, Float.floatToRawIntBits(value));
|
||
}
|
||
public abstract float readFloat();
|
||
public float readFloatLE() {
|
||
return Float.intBitsToFloat(readIntLE());
|
||
}
|
||
public abstract ByteBuf writeFloat(float value);
|
||
public ByteBuf writeFloatLE(float value) {
|
||
return writeIntLE(Float.floatToRawIntBits(value));
|
||
}
|
||
|
||
// Double 8 字节
|
||
public abstract double getDouble(int index);
|
||
public double getDoubleLE(int index) {
|
||
return Double.longBitsToDouble(getLongLE(index));
|
||
}
|
||
public abstract ByteBuf setDouble(int index, double value);
|
||
public ByteBuf setDoubleLE(int index, double value) {
|
||
return setLongLE(index, Double.doubleToRawLongBits(value));
|
||
}
|
||
public abstract double readDouble();
|
||
public double readDoubleLE() {
|
||
return Double.longBitsToDouble(readLongLE());
|
||
}
|
||
public abstract ByteBuf writeDouble(double value);
|
||
public ByteBuf writeDoubleLE(double value) {
|
||
return writeLongLE(Double.doubleToRawLongBits(value));
|
||
}
|
||
|
||
// Byte 数组
|
||
public abstract ByteBuf getBytes(int index, ByteBuf dst);
|
||
public abstract ByteBuf getBytes(int index, ByteBuf dst, int length);
|
||
public abstract ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length);
|
||
public abstract ByteBuf getBytes(int index, byte[] dst);
|
||
public abstract ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length);
|
||
public abstract ByteBuf getBytes(int index, ByteBuffer dst);
|
||
public abstract ByteBuf getBytes(int index, OutputStream out, int length) throws IOException;
|
||
public abstract int getBytes(int index, GatheringByteChannel out, int length) throws IOException;
|
||
public abstract int getBytes(int index, FileChannel out, long position, int length) throws IOException;
|
||
public abstract ByteBuf setBytes(int index, ByteBuf src);
|
||
public abstract ByteBuf setBytes(int index, ByteBuf src, int length);
|
||
public abstract ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length);
|
||
public abstract ByteBuf setBytes(int index, byte[] src);
|
||
public abstract ByteBuf setBytes(int index, byte[] src, int srcIndex, int length);
|
||
public abstract ByteBuf setBytes(int index, ByteBuffer src);
|
||
public abstract int setBytes(int index, InputStream in, int length) throws IOException;
|
||
public abstract int setBytes(int index, ScatteringByteChannel in, int length) throws IOException;
|
||
public abstract int setBytes(int index, FileChannel in, long position, int length) throws IOException;
|
||
public abstract ByteBuf setZero(int index, int length);
|
||
public abstract ByteBuf readBytes(int length);
|
||
public abstract ByteBuf readSlice(int length);
|
||
public abstract ByteBuf readRetainedSlice(int length);
|
||
public abstract ByteBuf readBytes(ByteBuf dst);
|
||
public abstract ByteBuf readBytes(ByteBuf dst, int length);
|
||
public abstract ByteBuf readBytes(ByteBuf dst, int dstIndex, int length);
|
||
public abstract ByteBuf readBytes(byte[] dst);
|
||
public abstract ByteBuf readBytes(byte[] dst, int dstIndex, int length);
|
||
public abstract ByteBuf readBytes(ByteBuffer dst);
|
||
public abstract ByteBuf readBytes(OutputStream out, int length) throws IOException;
|
||
public abstract int readBytes(GatheringByteChannel out, int length) throws IOException;
|
||
public abstract int readBytes(FileChannel out, long position, int length) throws IOException;
|
||
public abstract ByteBuf skipBytes(int length); // 忽略指定长度的字节数
|
||
public abstract ByteBuf writeBytes(ByteBuf src);
|
||
public abstract ByteBuf writeBytes(ByteBuf src, int length);
|
||
public abstract ByteBuf writeBytes(ByteBuf src, int srcIndex, int length);
|
||
public abstract ByteBuf writeBytes(byte[] src);
|
||
public abstract ByteBuf writeBytes(byte[] src, int srcIndex, int length);
|
||
public abstract ByteBuf writeBytes(ByteBuffer src);
|
||
public abstract int writeBytes(InputStream in, int length) throws IOException;
|
||
public abstract int writeBytes(ScatteringByteChannel in, int length) throws IOException;
|
||
public abstract int writeBytes(FileChannel in, long position, int length) throws IOException;
|
||
public abstract ByteBuf writeZero(int length); // 填充指定长度的 0
|
||
|
||
// String
|
||
public abstract CharSequence getCharSequence(int index, int length, Charset charset);
|
||
public abstract int setCharSequence(int index, CharSequence sequence, Charset charset);
|
||
public abstract CharSequence readCharSequence(int length, Charset charset);
|
||
public abstract int writeCharSequence(CharSequence sequence, Charset charset);
|
||
```
|
||
|
||
虽然方法比较多,总结下来是不同数据类型的**四种**读写方法:
|
||
|
||
- `#getXXX(index)` 方法,读取**指定**位置的数据,不改变 `readerIndex` 索引。
|
||
- `#readXXX()` 方法,读取 `readerIndex` 位置的数据,会改成 `readerIndex` 索引。
|
||
- `#setXXX(index, value)` 方法,写入数据到**指定**位置,不改变 `writeIndex` 索引。
|
||
- `#writeXXX(value)` 方法,写入数据到**指定**位置,会改变 `writeIndex` 索引。
|
||
|
||
### 2.1.3 查找 / 遍历操作
|
||
|
||
```
|
||
public abstract int indexOf(int fromIndex, int toIndex, byte value); // 指定值( value ) 在 ByteBuf 中的位置
|
||
public abstract int bytesBefore(byte value);
|
||
public abstract int bytesBefore(int length, byte value);
|
||
public abstract int bytesBefore(int index, int length, byte value);
|
||
|
||
public abstract int forEachByte(ByteProcessor processor); // 遍历 ByteBuf ,进行自定义处理
|
||
public abstract int forEachByte(int index, int length, ByteProcessor processor);
|
||
public abstract int forEachByteDesc(ByteProcessor processor);
|
||
public abstract int forEachByteDesc(int index, int length, ByteProcessor processor);
|
||
```
|
||
|
||
### 3.1.4 释放操作
|
||
|
||
```
|
||
public abstract ByteBuf discardReadBytes(); // 释放已读的字节空间
|
||
public abstract ByteBuf discardSomeReadBytes(); // 释放部分已读的字节空间
|
||
|
||
public abstract ByteBuf clear(); // 清空字节空间。实际是修改 readerIndex=writerIndex=0,标记清空。
|
||
```
|
||
|
||
**discardReadBytes**
|
||
|
||
`#discardReadBytes()` 方法,释放【所有的】**废弃段**的空间内存。
|
||
|
||
- 优点:达到重用废弃段的空间内存。
|
||
- 缺点:释放的方式,是通过复制**可读段**到 ByteBuf 的头部。所以,频繁释放会导致性能下降。
|
||
- 总结:这是典型的问题:选择空间还是时间。具体的选择,需要看对应的场景。😈 后续的文章,我们会看到对该方法的调用。
|
||
|
||
整个过程如下图:[简介.assets/02.png)](http://static.iocoder.cn/images/Netty/2018_08_01/02.png)discardReadBytes
|
||
|
||
**discardSomeReadBytes**
|
||
|
||
`#discardSomeReadBytes()` 方法,释放【部分的】**废弃段**的空间内存。
|
||
|
||
这是对 `#discardSomeReadBytes()` 方法的这种方案,具体的实现,见 [「4. AbstractByteBuf」](https://svip.iocoder.cn/Netty/ByteBuf-1-1-ByteBuf-intro/#) 中。
|
||
|
||
**clear**
|
||
|
||
`#clear()` 方法,清空字节空间。实际是修改 `readerIndex = writerIndex = 0` ,标记清空。
|
||
|
||
- 优点:通过标记来实现清空,避免置空 ByteBuf ,提升性能。
|
||
- 缺点:数据实际还存在,如果错误修改 `writerIndex` 时,会导致读到“脏”数据。
|
||
|
||
整个过程如下图:[简介.assets/03.png)](http://static.iocoder.cn/images/Netty/2018_08_01/03.png)discardReadBytes
|
||
|
||
### 3.1.5 拷贝操作
|
||
|
||
```
|
||
public abstract ByteBuf copy(); // 拷贝可读部分的字节数组。独立,互相不影响。
|
||
public abstract ByteBuf copy(int index, int length);
|
||
|
||
public abstract ByteBuf slice(); // 拷贝可读部分的字节数组。共享,相互影响。
|
||
public abstract ByteBuf slice(int index, int length);
|
||
public abstract ByteBuf retainedSlice();
|
||
|
||
public abstract ByteBuf duplicate(); // 拷贝整个的字节数组。共享,相互影响。
|
||
public abstract ByteBuf retainedDuplicate();
|
||
```
|
||
|
||
### 3.1.6 转换 NIO ByteBuffer 操作
|
||
|
||
```
|
||
// ByteBuf 包含 ByteBuffer 数量。
|
||
// 如果返回 = 1 ,则调用 `#nioBuffer()` 方法,获得 ByteBuf 包含的 ByteBuffer 对象。
|
||
// 如果返回 > 1 ,则调用 `#nioBuffers()` 方法,获得 ByteBuf 包含的 ByteBuffer 数组。
|
||
public abstract int nioBufferCount();
|
||
|
||
public abstract ByteBuffer nioBuffer();
|
||
public abstract ByteBuffer nioBuffer(int index, int length);
|
||
public abstract ByteBuffer internalNioBuffer(int index, int length);
|
||
|
||
public abstract ByteBuffer[] nioBuffers();
|
||
public abstract ByteBuffer[] nioBuffers(int index, int length);
|
||
```
|
||
|
||
### 3.1.7 Heap 相关方法
|
||
|
||
```
|
||
// 适用于 Heap 类型的 ByteBuf 对象的 byte[] 字节数组
|
||
public abstract boolean hasArray(); // 是否有 byte[] 字节数组
|
||
public abstract byte[] array();
|
||
public abstract int arrayOffset();
|
||
```
|
||
|
||
- 详细解析,见 [《精尽 Netty 源码解析 —— Buffer 之 ByteBuf(二)核心子类》](http://svip.iocoder.cn/Netty/ByteBuf-1-2-ByteBuf-core-impl)
|
||
|
||
### 3.1.8 Unsafe 相关方法
|
||
|
||
```
|
||
// 适用于 Unsafe 类型的 ByteBuf 对象
|
||
public abstract boolean hasMemoryAddress(); // 是否有内存地址
|
||
public abstract long memoryAddress();
|
||
```
|
||
|
||
- 详细解析,见 [《精尽 Netty 源码解析 —— Buffer 之 ByteBuf(二)核心子类》](http://svip.iocoder.cn/Netty/ByteBuf-1-2-ByteBuf-core-impl)
|
||
|
||
### 3.1.9 Object 相关
|
||
|
||
```
|
||
@Override
|
||
public abstract String toString();
|
||
public abstract String toString(Charset charset);
|
||
public abstract String toString(int index, int length, Charset charset);
|
||
|
||
@Override
|
||
public abstract int hashCode();
|
||
|
||
@Override
|
||
public abstract boolean equals(Object obj);
|
||
|
||
@Override
|
||
public abstract int compareTo(ByteBuf buffer);
|
||
```
|
||
|
||
### 3.1.10 引用计数相关
|
||
|
||
本文暂时不解析,我们会在 TODO 1011 。
|
||
|
||
来自 ReferenceCounted
|
||
|
||
https://skyao.gitbooks.io/learning-netty/content/buffer/interface_ReferenceCounted.html 可参考
|
||
|
||
```
|
||
@Override
|
||
public abstract ByteBuf retain(int increment);
|
||
@Override
|
||
public abstract ByteBuf retain();
|
||
|
||
@Override
|
||
public abstract ByteBuf touch();
|
||
@Override
|
||
public abstract ByteBuf touch(Object hint);
|
||
```
|
||
|
||
## 3.2 子类类图
|
||
|
||
ByteBuf 的子类灰常灰常灰常多,胖友点击 [传送门](http://static.iocoder.cn/images/Netty/2018_08_01/04.png) 可以进行查看。
|
||
|
||
本文仅分享 ByteBuf 的**五个**直接子类实现,如下图所示:[简介.assets/05.png)](http://static.iocoder.cn/images/Netty/2018_08_01/05.png)传送门
|
||
|
||
- 【重点】AbstractByteBuf ,ByteBuf 抽象实现类,提供 ByteBuf 的默认实现类。可以说,是 ByteBuf 最最最重要的子类。详细解析,见 [「4. AbstractByteBuf」](https://svip.iocoder.cn/Netty/ByteBuf-1-1-ByteBuf-intro/#) 。
|
||
- EmptyByteBuf ,用于构建空 ByteBuf 对象,`capacity` 和 `maxCapacity` 均为 0 。详细解析,见 [「5. EmptyByteBuf」](https://svip.iocoder.cn/Netty/ByteBuf-1-1-ByteBuf-intro/#) 。
|
||
- WrappedByteBuf ,用于装饰 ByteBuf 对象。详细解析,见 [「6. WrappedByteBuf」](https://svip.iocoder.cn/Netty/ByteBuf-1-1-ByteBuf-intro/#) 。
|
||
- SwappedByteBuf ,用于构建具有切换**字节序**功能的 ByteBuf 对象。详细解析,见 [「7. SwappedByteBuf」](https://svip.iocoder.cn/Netty/ByteBuf-1-1-ByteBuf-intro/#) 。
|
||
- ReplayingDecoderByteBuf ,用于构建在 IO 阻塞条件下实现无阻塞解码的特殊 ByteBuf 对象,当要读取的数据还未接收完全时,抛出异常,交由 ReplayingDecoder处理。详细解析,见 [「8. ReplayingDecoderByteBuf」](https://svip.iocoder.cn/Netty/ByteBuf-1-1-ByteBuf-intro/#) 。
|
||
|
||
# 4. AbstractByteBuf
|
||
|
||
`io.netty.buffer.AbstractByteBuf` ,实现 ByteBuf 抽象类,ByteBuf 抽象实现类。官方注释如下:
|
||
|
||
```
|
||
/**
|
||
* A skeletal implementation of a buffer.
|
||
*/
|
||
```
|
||
|
||
因为 AbstractByteBuf 实现类 ByteBuf 超级多的方法,所以我们还是按照 ByteBuf 的归类,逐个分析过去。
|
||
|
||
## 4.1 基础信息
|
||
|
||
### 4.1.1 构造方法
|
||
|
||
```
|
||
/**
|
||
* 读取位置
|
||
*/
|
||
int readerIndex;
|
||
/**
|
||
* 写入位置
|
||
*/
|
||
int writerIndex;
|
||
/**
|
||
* {@link #readerIndex} 的标记
|
||
*/
|
||
private int markedReaderIndex;
|
||
/**
|
||
* {@link #writerIndex} 的标记
|
||
*/
|
||
private int markedWriterIndex;
|
||
/**
|
||
* 最大容量
|
||
*/
|
||
private int maxCapacity;
|
||
|
||
protected AbstractByteBuf(int maxCapacity) {
|
||
if (maxCapacity < 0) {
|
||
throw new IllegalArgumentException("maxCapacity: " + maxCapacity + " (expected: >= 0)");
|
||
}
|
||
this.maxCapacity = maxCapacity;
|
||
}
|
||
```
|
||
|
||
- `capacity` 属性,在 AbstractByteBuf 未定义,而是由子类来实现。为什么呢?在后面的文章,我们会看到,ByteBuf 根据**内存类型**分成 Heap 和 Direct ,它们获取 `capacity` 的值的方式不同。
|
||
|
||
- `maxCapacity` 属性,相关的方法:
|
||
|
||
```
|
||
@Override
|
||
public int maxCapacity() {
|
||
return maxCapacity;
|
||
}
|
||
|
||
protected final void maxCapacity(int maxCapacity) {
|
||
this.maxCapacity = maxCapacity;
|
||
}
|
||
```
|
||
|
||
### 4.1.2 读索引相关的方法
|
||
|
||
**获取和设置读位置**
|
||
|
||
```
|
||
@Override
|
||
public int readerIndex() {
|
||
return readerIndex;
|
||
}
|
||
|
||
@Override
|
||
public ByteBuf readerIndex(int readerIndex) {
|
||
if (readerIndex < 0 || readerIndex > writerIndex) {
|
||
throw new IndexOutOfBoundsException(String.format(
|
||
"readerIndex: %d (expected: 0 <= readerIndex <= writerIndex(%d))", readerIndex, writerIndex));
|
||
}
|
||
this.readerIndex = readerIndex;
|
||
return this;
|
||
}
|
||
```
|
||
|
||
------
|
||
|
||
**是否可读**
|
||
|
||
```
|
||
@Override
|
||
public boolean isReadable() {
|
||
return writerIndex > readerIndex;
|
||
}
|
||
@Override
|
||
public boolean isReadable(int numBytes) {
|
||
return writerIndex - readerIndex >= numBytes;
|
||
}
|
||
|
||
@Override
|
||
public int readableBytes() {
|
||
return writerIndex - readerIndex;
|
||
}
|
||
```
|
||
|
||
------
|
||
|
||
**标记和重置读位置**
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf markReaderIndex() {
|
||
markedReaderIndex = readerIndex;
|
||
return this;
|
||
}
|
||
|
||
@Override
|
||
public ByteBuf resetReaderIndex() {
|
||
readerIndex(markedReaderIndex);
|
||
return this;
|
||
}
|
||
```
|
||
|
||
### 4.1.3 写索引相关的方法
|
||
|
||
**获取和设置写位置**
|
||
|
||
```
|
||
@Override
|
||
public int writerIndex() {
|
||
return writerIndex;
|
||
}
|
||
|
||
@Override
|
||
public ByteBuf writerIndex(int writerIndex) {
|
||
if (writerIndex < readerIndex || writerIndex > capacity()) {
|
||
throw new IndexOutOfBoundsException(String.format(
|
||
"writerIndex: %d (expected: readerIndex(%d) <= writerIndex <= capacity(%d))",
|
||
writerIndex, readerIndex, capacity()));
|
||
}
|
||
this.writerIndex = writerIndex;
|
||
return this;
|
||
}
|
||
```
|
||
|
||
------
|
||
|
||
**是否可写**
|
||
|
||
```
|
||
@Override
|
||
public boolean isWritable() {
|
||
return capacity() > writerIndex;
|
||
}
|
||
@Override
|
||
public boolean isWritable(int numBytes) {
|
||
return capacity() - writerIndex >= numBytes;
|
||
}
|
||
|
||
@Override
|
||
public int writableBytes() {
|
||
return capacity() - writerIndex;
|
||
}
|
||
@Override
|
||
public int maxWritableBytes() {
|
||
return maxCapacity() - writerIndex;
|
||
}
|
||
```
|
||
|
||
------
|
||
|
||
**标记和重置写位置**
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf markWriterIndex() {
|
||
markedWriterIndex = writerIndex;
|
||
return this;
|
||
}
|
||
|
||
@Override
|
||
public ByteBuf resetWriterIndex() {
|
||
writerIndex(markedWriterIndex);
|
||
return this;
|
||
}
|
||
```
|
||
|
||
------
|
||
|
||
**保证可写**
|
||
|
||
`#ensureWritable(int minWritableBytes)` 方法,保证有足够的可写空间。若不够,则进行扩容。代码如下:
|
||
|
||
```
|
||
1: @Override
|
||
2: public ByteBuf ensureWritable(int minWritableBytes) {
|
||
3: if (minWritableBytes < 0) {
|
||
4: throw new IllegalArgumentException(String.format(
|
||
5: "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
|
||
6: }
|
||
7: ensureWritable0(minWritableBytes);
|
||
8: return this;
|
||
9: }
|
||
10:
|
||
11: final void ensureWritable0(int minWritableBytes) {
|
||
12: // 检查是否可访问
|
||
13: ensureAccessible();
|
||
14: // 目前容量可写,直接返回
|
||
15: if (minWritableBytes <= writableBytes()) {
|
||
16: return;
|
||
17: }
|
||
18:
|
||
19: // 超过最大上限,抛出 IndexOutOfBoundsException 异常
|
||
20: if (minWritableBytes > maxCapacity - writerIndex) {
|
||
21: throw new IndexOutOfBoundsException(String.format(
|
||
22: "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
|
||
23: writerIndex, minWritableBytes, maxCapacity, this));
|
||
24: }
|
||
25:
|
||
26: // 计算新的容量。默认情况下,2 倍扩容,并且不超过最大容量上限。
|
||
27: // Normalize the current capacity to the power of 2.
|
||
28: int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
|
||
29:
|
||
30: // 设置新的容量大小
|
||
31: // Adjust to the new capacity.
|
||
32: capacity(newCapacity);
|
||
33: }
|
||
```
|
||
|
||
- 第 13 行:调用 `#ensureAccessible()` 方法,检查是否可访问。代码如下:
|
||
|
||
```
|
||
/**
|
||
* Should be called by every method that tries to access the buffers content to check
|
||
* if the buffer was released before.
|
||
*/
|
||
protected final void ensureAccessible() {
|
||
if (checkAccessible && refCnt() == 0) { // 若指向为 0 ,说明已经释放,不可继续写入。
|
||
throw new IllegalReferenceCountException(0);
|
||
}
|
||
}
|
||
|
||
private static final String PROP_MODE = "io.netty.buffer.bytebuf.checkAccessible";
|
||
/**
|
||
* 是否检查可访问
|
||
*
|
||
* @see #ensureAccessible()
|
||
*/
|
||
private static final boolean checkAccessible;
|
||
|
||
static {
|
||
checkAccessible = SystemPropertyUtil.getBoolean(PROP_MODE, true);
|
||
if (logger.isDebugEnabled()) {
|
||
logger.debug("-D{}: {}", PROP_MODE, checkAccessible);
|
||
}
|
||
}
|
||
```
|
||
|
||
- 第 14 至 17 行:目前容量可写,直接返回。
|
||
|
||
- 第 19 至 24 行:超过最大上限,抛出 IndexOutOfBoundsException 异常。
|
||
|
||
- 第 28 行:调用
|
||
|
||
|
||
|
||
```
|
||
ByteBufAllocator#calculateNewCapacity(int minNewCapacity, int maxCapacity)
|
||
```
|
||
|
||
|
||
|
||
方法,计算新的容量。默认情况下,2 倍扩容,并且不超过最大容量上限。
|
||
|
||
注意
|
||
|
||
,此处仅仅是计算,并没有扩容内存复制等等操作。
|
||
|
||
- 第 32 行:调用 `#capacity(newCapacity)` 方法,设置新的容量大小。
|
||
|
||
`#ensureWritable(int minWritableBytes, boolean force)` 方法,保证有足够的可写空间。若不够,则进行扩容。代码如下:
|
||
|
||
```
|
||
@Override
|
||
public int ensureWritable(int minWritableBytes, boolean force) {
|
||
// 检查是否可访问
|
||
ensureAccessible();
|
||
if (minWritableBytes < 0) {
|
||
throw new IllegalArgumentException(String.format(
|
||
"minWritableBytes: %d (expected: >= 0)", minWritableBytes));
|
||
}
|
||
|
||
// 目前容量可写,直接返回 0
|
||
if (minWritableBytes <= writableBytes()) {
|
||
return 0;
|
||
}
|
||
|
||
final int maxCapacity = maxCapacity();
|
||
final int writerIndex = writerIndex();
|
||
// 超过最大上限
|
||
if (minWritableBytes > maxCapacity - writerIndex) {
|
||
// 不强制设置,或者已经到达最大容量
|
||
if (!force || capacity() == maxCapacity) {
|
||
// 返回 1
|
||
return 1;
|
||
}
|
||
|
||
// 设置为最大容量
|
||
capacity(maxCapacity);
|
||
// 返回 3
|
||
return 3;
|
||
}
|
||
|
||
// 计算新的容量。默认情况下,2 倍扩容,并且不超过最大容量上限。
|
||
// Normalize the current capacity to the power of 2.
|
||
int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
|
||
|
||
// 设置新的容量大小
|
||
// Adjust to the new capacity.
|
||
capacity(newCapacity);
|
||
|
||
// 返回 2
|
||
return 2;
|
||
}
|
||
```
|
||
|
||
和 `#ensureWritable(int minWritableBytes)` 方法,有两点不同:
|
||
|
||
- 超过最大容量的上限时,不会抛出 IndexOutOfBoundsException 异常。
|
||
- 根据执行的过程不同,返回不同的返回值。
|
||
|
||
比较简单,胖友自己看下代码。
|
||
|
||
### 4.1.4 setIndex
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf setIndex(int readerIndex, int writerIndex) {
|
||
if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) {
|
||
throw new IndexOutOfBoundsException(String.format(
|
||
"readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
|
||
readerIndex, writerIndex, capacity()));
|
||
}
|
||
setIndex0(readerIndex, writerIndex);
|
||
return this;
|
||
}
|
||
|
||
final void setIndex0(int readerIndex, int writerIndex) {
|
||
this.readerIndex = readerIndex;
|
||
this.writerIndex = writerIndex;
|
||
}
|
||
```
|
||
|
||
### 4.1.5 读索引标记位相关的方法
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf markReaderIndex() {
|
||
markedReaderIndex = readerIndex;
|
||
return this;
|
||
}
|
||
|
||
@Override
|
||
public ByteBuf resetReaderIndex() {
|
||
readerIndex(markedReaderIndex);
|
||
return this;
|
||
}
|
||
```
|
||
|
||
### 4.1.6 写索引标记位相关的方法
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf markWriterIndex() {
|
||
markedWriterIndex = writerIndex;
|
||
return this;
|
||
}
|
||
|
||
@Override
|
||
public ByteBuf resetWriterIndex() {
|
||
writerIndex(markedWriterIndex);
|
||
return this;
|
||
}
|
||
```
|
||
|
||
### 4.1.7 是否只读相关
|
||
|
||
`#isReadOnly()` 方法,返回是否只读。代码如下:
|
||
|
||
```
|
||
@Override
|
||
public boolean isReadOnly() {
|
||
return false;
|
||
}
|
||
```
|
||
|
||
- 默认返回 `false` 。子类可覆写该方法,根据情况返回结果。
|
||
|
||
------
|
||
|
||
`#asReadOnly()` 方法,转换成只读 ByteBuf 对象。代码如下:
|
||
|
||
```
|
||
@SuppressWarnings("deprecation")
|
||
@Override
|
||
public ByteBuf asReadOnly() {
|
||
// 如果是只读,直接返回
|
||
if (isReadOnly()) {
|
||
return this;
|
||
}
|
||
// 转化成只读 Buffer 对象
|
||
return Unpooled.unmodifiableBuffer(this);
|
||
}
|
||
```
|
||
|
||
- 如果已是只读,直接返回该 ByteBuf 对象。
|
||
|
||
- 如果不是只读,调用 `Unpooled#unmodifiableBuffer(Bytebuf)` 方法,转化成只读 Buffer 对象。代码如下:
|
||
|
||
```
|
||
/**
|
||
* Creates a read-only buffer which disallows any modification operations
|
||
* on the specified {@code buffer}. The new buffer has the same
|
||
* {@code readerIndex} and {@code writerIndex} with the specified
|
||
* {@code buffer}.
|
||
*
|
||
* @deprecated Use {@link ByteBuf#asReadOnly()}.
|
||
*/
|
||
@Deprecated
|
||
public static ByteBuf unmodifiableBuffer(ByteBuf buffer) {
|
||
ByteOrder endianness = buffer.order();
|
||
// 大端
|
||
if (endianness == BIG_ENDIAN) {
|
||
return new ReadOnlyByteBuf(buffer);
|
||
}
|
||
// 小端
|
||
return new ReadOnlyByteBuf(buffer.order(BIG_ENDIAN)).order(LITTLE_ENDIAN);
|
||
}
|
||
```
|
||
|
||
- 注意,返回的是**新的** `io.netty.buffer.ReadOnlyByteBuf` 对象。并且,和原 ByteBuf 对象,共享 `readerIndex` 和 `writerIndex` 索引,以及相关的数据。仅仅是说,只读,不能写入。
|
||
|
||
### 4.1.8 ByteOrder 相关的方法
|
||
|
||
`#order()` 方法,获得字节序。由子类实现,因为 AbstractByteBuf 的内存类型,不确定是 Heap 还是 Direct 。
|
||
|
||
------
|
||
|
||
`#order(ByteOrder endianness)` 方法,设置字节序。代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf order(ByteOrder endianness) {
|
||
if (endianness == null) {
|
||
throw new NullPointerException("endianness");
|
||
}
|
||
// 未改变,直接返回
|
||
if (endianness == order()) {
|
||
return this;
|
||
}
|
||
// 创建 SwappedByteBuf 对象
|
||
return newSwappedByteBuf();
|
||
}
|
||
|
||
/**
|
||
* Creates a new {@link SwappedByteBuf} for this {@link ByteBuf} instance.
|
||
*/
|
||
protected SwappedByteBuf newSwappedByteBuf() {
|
||
return new SwappedByteBuf(this);
|
||
}
|
||
```
|
||
|
||
- 如果字节序未修改,直接返回该 ByteBuf 对象。
|
||
- 如果字节序有修改,调用 `#newSwappedByteBuf()` 方法,TODO SwappedByteBuf
|
||
|
||
### 4.1.9 未实现方法
|
||
|
||
和 [「2.1.1 基础信息」](https://svip.iocoder.cn/Netty/ByteBuf-1-1-ByteBuf-intro/#) 相关的方法,有三个未实现,如下:
|
||
|
||
```
|
||
public abstract ByteBufAllocator alloc(); // 分配器,用于创建 ByteBuf 对象。
|
||
|
||
public abstract ByteBuf unwrap(); // 获得被包装( wrap )的 ByteBuf 对象。
|
||
|
||
public abstract boolean isDirect(); // 是否 NIO Direct Buffer
|
||
```
|
||
|
||
## 4.2 读取 / 写入操作
|
||
|
||
我们以 Int 类型为例子,来看看它的读取和写入操作的实现代码。
|
||
|
||
### 4.2.1 getInt
|
||
|
||
```
|
||
@Override
|
||
public int getInt(int index) {
|
||
// 校验读取是否会超过容量
|
||
checkIndex(index, 4);
|
||
// 读取 Int 数据
|
||
return _getInt(index);
|
||
}
|
||
```
|
||
|
||
- 调用 `#checkIndex(index, fieldLength)` 方法,校验读取是否会超过**容量**。注意,不是超过 `writerIndex` 位置。因为,只是读取指定位置开始的 Int 数据,不会改变 `readerIndex` 。代码如下:
|
||
|
||
```
|
||
protected final void checkIndex(int index, int fieldLength) {
|
||
// 校验是否可访问
|
||
ensureAccessible();
|
||
// 校验是否会超过容量
|
||
checkIndex0(index, fieldLength);
|
||
}
|
||
final void checkIndex0(int index, int fieldLength) {
|
||
if (isOutOfBounds(index, fieldLength, capacity())) {
|
||
throw new IndexOutOfBoundsException(String.format(
|
||
"index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity()));
|
||
}
|
||
}
|
||
|
||
// MathUtil.java
|
||
/**
|
||
* Determine if the requested {@code index} and {@code length} will fit within {@code capacity}.
|
||
* @param index The starting index.
|
||
* @param length The length which will be utilized (starting from {@code index}).
|
||
* @param capacity The capacity that {@code index + length} is allowed to be within.
|
||
* @return {@code true} if the requested {@code index} and {@code length} will fit within {@code capacity}.
|
||
* {@code false} if this would result in an index out of bounds exception.
|
||
*/
|
||
public static boolean isOutOfBounds(int index, int length, int capacity) {
|
||
// 只有有负数,或运算,就会有负数。
|
||
// 另外,此处的越界,不仅仅有 capacity - (index + length < 0 ,例如 index < 0 ,也是越界
|
||
return (index | length | (index + length) | (capacity - (index + length))) < 0;
|
||
}
|
||
```
|
||
|
||
- 调用 `#_getInt(index)` 方法,读取 Int 数据。这是一个**抽象**方法,由子类实现。代码如下:
|
||
|
||
```
|
||
protected abstract int _getInt(int index);
|
||
```
|
||
|
||
关于 `#getIntLE(int index)` / `getUnsignedInt(int index)` / `getUnsignedIntLE(int index)` 方法的实现,胖友自己去看。
|
||
|
||
### 4.2.2 readInt
|
||
|
||
```
|
||
@Override
|
||
public int readInt() {
|
||
// 校验读取是否会超过可读段
|
||
checkReadableBytes0(4);
|
||
// 读取 Int 数据
|
||
int v = _getInt(readerIndex);
|
||
// 修改 readerIndex ,加上已读取字节数
|
||
readerIndex += 4;
|
||
return v;
|
||
}
|
||
```
|
||
|
||
- 调用 `#checkReadableBytes0(fieldLength)` 方法,校验读取是否会超过**可读段**。代码如下:
|
||
|
||
```
|
||
private void checkReadableBytes0(int minimumReadableBytes) {
|
||
// 是否可访问
|
||
ensureAccessible();
|
||
// 是否超过写索引,即超过可读段
|
||
if (readerIndex > writerIndex - minimumReadableBytes) {
|
||
throw new IndexOutOfBoundsException(String.format(
|
||
"readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s",
|
||
readerIndex, minimumReadableBytes, writerIndex, this));
|
||
}
|
||
}
|
||
```
|
||
|
||
- 调用 `#_getInt(index)` 方法,读取 Int 数据。
|
||
|
||
- 读取完成,修改 `readerIndex` 【**重要** 😈】,加上已读取字节数 4 。
|
||
|
||
关于 `#readIntLE()` / `readUnsignedInt()` / `readUnsignedIntLE()` 方法的实现,胖友自己去看。
|
||
|
||
### 4.2.3 setInt
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf setInt(int index, int value) {
|
||
// 校验写入是否会超过容量
|
||
checkIndex(index, 4);
|
||
// 设置 Int 数据
|
||
_setInt(index, value);
|
||
return this;
|
||
}
|
||
```
|
||
|
||
- 调用 `#checkIndex(index, fieldLength)` 方法,校验写入是否会超过**容量**。
|
||
|
||
- 调用 `#_setInt(index,value )` 方法,写入 Int 数据。这是一个**抽象**方法,由子类实现。代码如下:
|
||
|
||
```
|
||
protected abstract int _setInt(int index, int value);
|
||
```
|
||
|
||
关于 `#setIntLE(int index, int value)` 方法的实现,胖友自己去看。
|
||
|
||
public abstract ByteBuf writeInt(int value);
|
||
public abstract ByteBuf writeIntLE(int value);
|
||
|
||
### 4.2.4 writeInt
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf writeInt(int value) {
|
||
// 保证可写入
|
||
ensureWritable0(4);
|
||
// 写入 Int 数据
|
||
_setInt(writerIndex, value);
|
||
// 修改 writerIndex ,加上已写入字节数
|
||
writerIndex += 4;
|
||
return this;
|
||
}
|
||
```
|
||
|
||
- 调用 `#ensureWritable0(int minWritableBytes)` 方法,保证可写入。
|
||
- 调用 `#_setInt(index, int value)` 方法,写入Int 数据。
|
||
- 写入完成,修改 `writerIndex` 【**重要** 😈】,加上已写入字节数 4 。
|
||
|
||
### 4.2.5 其它方法
|
||
|
||
其它类型的读取和写入操作的实现代码,胖友自己研究落。还是有一些有意思的方法,例如:
|
||
|
||
- `#writeZero(int length)` 方法。原本以为是循环 `length` 次写入 0 字节,结果发现会基于 `long` => `int` => `byte` 的顺序,尽可能合并写入。
|
||
- `#skipBytes((int length)` 方法
|
||
|
||
## 4.3 查找 / 遍历操作
|
||
|
||
查找 / 遍历操作相关的方法,实现比较简单。所以,感兴趣的胖友,可以自己去看。
|
||
|
||
## 4.4 释放操作
|
||
|
||
### 4.4.1 discardReadBytes
|
||
|
||
`#discardReadBytes()` 方法,代码如下:
|
||
|
||
```
|
||
1: @Override
|
||
2: public ByteBuf discardReadBytes() {
|
||
3: // 校验可访问
|
||
4: ensureAccessible();
|
||
5: // 无废弃段,直接返回
|
||
6: if (readerIndex == 0) {
|
||
7: return this;
|
||
8: }
|
||
9:
|
||
10: // 未读取完
|
||
11: if (readerIndex != writerIndex) {
|
||
12: // 将可读段复制到 ByteBuf 头
|
||
13: setBytes(0, this, readerIndex, writerIndex - readerIndex);
|
||
14: // 写索引减小
|
||
15: writerIndex -= readerIndex;
|
||
16: // 调整标记位
|
||
17: adjustMarkers(readerIndex);
|
||
18: // 读索引重置为 0
|
||
19: readerIndex = 0;
|
||
20: // 全部读取完
|
||
21: } else {
|
||
22: // 调整标记位
|
||
23: adjustMarkers(readerIndex);
|
||
24: // 读写索引都重置为 0
|
||
25: writerIndex = readerIndex = 0;
|
||
26: }
|
||
27: return this;
|
||
28: }
|
||
```
|
||
|
||
- 第 4 行:调用 `#ensureAccessible()` 方法,检查是否可访问。
|
||
|
||
- 第 5 至 8 行:无**废弃段**,直接返回。
|
||
|
||
- 第 10 至 19 行:未读取完,即还有**可读段**。
|
||
|
||
- 第 13 行:调用 `#setBytes(int index, ByteBuf src, int srcIndex, int length)` 方法,将可读段复制到 ByteBuf 头开始。如下图所示:[简介.assets/02.png)](http://static.iocoder.cn/images/Netty/2018_08_01/02.png)discardReadBytes
|
||
|
||
- 第 15 行:写索引 `writerIndex` 减小。
|
||
|
||
- 第 19 行:调用 `#adjustMarkers(int decrement)` 方法,调整标记位。代码如下:
|
||
|
||
```
|
||
protected final void adjustMarkers(int decrement) {
|
||
int markedReaderIndex = this.markedReaderIndex;
|
||
// 读标记位小于减少值(decrement)
|
||
if (markedReaderIndex <= decrement) {
|
||
// 重置读标记位为 0
|
||
this.markedReaderIndex = 0;
|
||
// 写标记位小于减少值(decrement)
|
||
int markedWriterIndex = this.markedWriterIndex;
|
||
if (markedWriterIndex <= decrement) {
|
||
// 重置写标记位为 0
|
||
this.markedWriterIndex = 0;
|
||
// 减小写标记位
|
||
} else {
|
||
this.markedWriterIndex = markedWriterIndex - decrement;
|
||
}
|
||
// 减小读写标记位
|
||
} else {
|
||
this.markedReaderIndex = markedReaderIndex - decrement;
|
||
this.markedWriterIndex -= decrement;
|
||
}
|
||
}
|
||
```
|
||
|
||
- 代码虽然比较多,但是目的很明确,**减小**读写标记位。并且,通过判断,**最多减小至 0** 。
|
||
|
||
- 第 19 行:**仅**读索引重置为 0 。
|
||
|
||
- 第 20 至 26 行:全部读取完,即无
|
||
|
||
可读段
|
||
|
||
。
|
||
|
||
- 第 23 行:调用 `#adjustMarkers(int decrement)` 方法,调整标记位。
|
||
- 第 25 行:读写索引**都**重置为 0 。
|
||
|
||
### 4.4.2 discardSomeReadBytes
|
||
|
||
`#discardSomeReadBytes()` 方法,代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf discardSomeReadBytes() {
|
||
// 校验可访问
|
||
ensureAccessible();
|
||
// 无废弃段,直接返回
|
||
if (readerIndex == 0) {
|
||
return this;
|
||
}
|
||
|
||
// 全部读取完
|
||
if (readerIndex == writerIndex) {
|
||
// 调整标记位
|
||
adjustMarkers(readerIndex);
|
||
// 读写索引都重置为 0
|
||
writerIndex = readerIndex = 0;
|
||
return this;
|
||
}
|
||
|
||
// 读取超过容量的一半,进行释放
|
||
if (readerIndex >= capacity() >>> 1) {
|
||
// 将可读段复制到 ByteBuf 头
|
||
setBytes(0, this, readerIndex, writerIndex - readerIndex);
|
||
// 写索引减小
|
||
writerIndex -= readerIndex;
|
||
// 调整标记位
|
||
adjustMarkers(readerIndex);
|
||
// 读索引重置为 0
|
||
readerIndex = 0;
|
||
}
|
||
return this;
|
||
}
|
||
```
|
||
|
||
整体代码和 `#discardReadBytes()` 方法是**一致的**。差别在于,`readerIndex >= capacity() >>> 1` ,读取超过容量的**一半**时,进行释放。也就是说,在空间和时间之间,做了一个平衡。
|
||
|
||
😈 后续,我们来看看,Netty 具体在什么时候,调用 `#discardSomeReadBytes()` 和 `#discardReadBytes()` 方法。
|
||
|
||
### 4.4.3 clear
|
||
|
||
`#clear()` 方法,代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf clear() {
|
||
readerIndex = writerIndex = 0;
|
||
return this;
|
||
}
|
||
```
|
||
|
||
- 读写索引**都**重置为 0 。
|
||
- 读写标记位**不会**重置。
|
||
|
||
## 4.5 拷贝操作
|
||
|
||
### 4.5.1 copy
|
||
|
||
`#copy()` 方法,拷贝可读部分的字节数组。代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf copy() {
|
||
return copy(readerIndex, readableBytes());
|
||
}
|
||
```
|
||
|
||
- 调用 `#readableBytes()` 方法,获得可读的字节数。
|
||
- 调用 `#copy(int index, int length)` 方法,拷贝**指定部分**的字节数组。独立,互相不影响。具体的实现,需要子类中实现,原因是做**深**拷贝,需要根据内存类型是 Heap 和 Direct 会有不同。
|
||
|
||
### 4.5.2 slice
|
||
|
||
`#slice()` 方法,拷贝可读部分的字节数组。代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf slice() {
|
||
return slice(readerIndex, readableBytes());
|
||
}
|
||
```
|
||
|
||
- 调用 `#readableBytes()` 方法,获得可读的字节数。
|
||
|
||
- 调用 `#slice(int index, int length)` 方法,拷贝**指定部分**的字节数组。共享,互相影响。代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf slice(int index, int length) {
|
||
// 校验可访问
|
||
ensureAccessible();
|
||
// 创建 UnpooledSlicedByteBuf 对象
|
||
return new UnpooledSlicedByteBuf(this, index, length);
|
||
}
|
||
```
|
||
|
||
- 返回的是创建的 UnpooledSlicedByteBuf 对象。在它内部,会调用当前 ByteBuf 对象,所以这也是为什么说是**共享**的。或者说,我们可以认为这是一个**浅**拷贝。
|
||
|
||
------
|
||
|
||
`#retainedSlice()` 方法,在 `#slice()` 方法的基础上,引用计数加 1 。代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf retainedSlice(int index, int length) {
|
||
return slice(index, length).retain();
|
||
}
|
||
```
|
||
|
||
- 调用 `#slice(int index, int length)` 方法,拷贝**指定部分**的字节数组。也就说,返回 UnpooledSlicedByteBuf 对象。
|
||
- 调用 `UnpooledSlicedByteBuf#retain()` 方法,,引用计数加 1 。本文暂时不解析,我们会在 TODO 1011 。
|
||
|
||
### 4.5.3 duplicate
|
||
|
||
`#duplicate()` 方法,拷贝**整个**的字节数组。代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf duplicate() {
|
||
// 校验是否可访问
|
||
ensureAccessible();
|
||
return new UnpooledDuplicatedByteBuf(this);
|
||
}
|
||
```
|
||
|
||
- 创建的 UnpooledDuplicatedByteBuf 对象。在它内部,会调用当前 ByteBuf 对象,所以这也是为什么说是**共享**的。或者说,我们可以认为这是一个**浅**拷贝。
|
||
- 它和 `#slice()` 方法的差别在于,前者是**整个**,后者是**可写段**。
|
||
|
||
------
|
||
|
||
`#retainedDuplicate()` 方法,在 `#duplicate()` 方法的基础上,引用计数加 1 。代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf retainedDuplicate() {
|
||
return duplicate().retain();
|
||
}
|
||
```
|
||
|
||
- 调用 `#duplicate()` 方法,拷贝**整个**的字节数组。也就说,返回 UnpooledDuplicatedByteBuf 对象。
|
||
- 调用 `UnpooledDuplicatedByteBuf#retain()` 方法,,引用计数加 1 。本文暂时不解析,我们会在 TODO 1011 。
|
||
|
||
## 4.6 转换 NIO ByteBuffer 操作
|
||
|
||
### 4.6.1 nioBuffer
|
||
|
||
`#nioBuffer()` 方法,代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuffer nioBuffer() {
|
||
return nioBuffer(readerIndex, readableBytes());
|
||
}
|
||
```
|
||
|
||
- 在方法内部,会调用 `#nioBuffer(int index, int length)` 方法。而该方法,由具体的子类实现。
|
||
|
||
> FROM [《深入研究Netty框架之ByteBuf功能原理及源码分析》](https://my.oschina.net/7001/blog/742236)
|
||
>
|
||
> 将当前 ByteBuf 的可读缓冲区( `readerIndex` 到 `writerIndex` 之间的内容) 转换为 ByteBuffer 对象,两者共享共享缓冲区的内容。对 ByteBuffer 的读写操作不会影响 ByteBuf 的读写索引。
|
||
>
|
||
> 注意:ByteBuffer 无法感知 ByteBuf 的动态扩展操作。ByteBuffer 的长度为`readableBytes()` 。
|
||
|
||
### 4.6.2 nioBuffers
|
||
|
||
`#nioBuffers()` 方法,代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuffer[] nioBuffers() {
|
||
return nioBuffers(readerIndex, readableBytes());
|
||
}
|
||
```
|
||
|
||
- 在方法内部,会调用 `#nioBuffers(int index, int length)` 方法。而该方法,由具体的子类实现。
|
||
- 😈 为什么会产生数组的情况呢?例如 CompositeByteBuf 。当然,后续文章,我们也会具体分享。
|
||
|
||
## 4.7 Heap 相关方法
|
||
|
||
Heap 相关方法,在子类中实现。详细解析,见 [《精尽 Netty 源码解析 —— Buffer 之 ByteBuf(二)核心子类》](http://svip.iocoder.cn/Netty/ByteBuf-1-2-ByteBuf-core-impl)
|
||
|
||
## 4.8 Unsafe 相关方法
|
||
|
||
Unsafe,在子类中实现。详细解析,见 [《精尽 Netty 源码解析 —— Buffer 之 ByteBuf(二)核心子类》](http://svip.iocoder.cn/Netty/ByteBuf-1-2-ByteBuf-core-impl)
|
||
|
||
## 4.9 Object 相关
|
||
|
||
Object 相关的方法,主要调用 `io.netty.buffer.ByteBufUtil` 进行实现。而 ByteUtil 是一个非常有用的工具类,它提供了一系列静态方法,用于操作 ByteBuf 对象:[简介.assets/06.png)](http://static.iocoder.cn/images/Netty/2018_08_01/06.png)ByteUtil
|
||
|
||
😈 因为 Object 相关的方法,实现比较简单。所以,感兴趣的胖友,可以自己去看。
|
||
|
||
## 4.10 引用计数相关
|
||
|
||
本文暂时不解析,我们会在 TODO 1011 。
|
||
|
||
# 5. EmptyByteBuf
|
||
|
||
`io.netty.buffer.EmptyByteBuf` ,继承 ByteBuf 抽象类,用于构建空 ByteBuf 对象,`capacity` 和 `maxCapacity` 均为 0 。
|
||
|
||
😈 代码实现超级简单,感兴趣的胖友,可以自己去看。
|
||
|
||
# 6. WrappedByteBuf
|
||
|
||
`io.netty.buffer.WrappedByteBuf` ,继承 ByteBuf 抽象类,用于装饰 ByteBuf 对象。构造方法如下:
|
||
|
||
```
|
||
/**
|
||
* 被装饰的 ByteBuf 对象
|
||
*/
|
||
protected final ByteBuf buf;
|
||
|
||
protected WrappedByteBuf(ByteBuf buf) {
|
||
if (buf == null) {
|
||
throw new NullPointerException("buf");
|
||
}
|
||
this.buf = buf;
|
||
}
|
||
```
|
||
|
||
- `buf` 属性,被装饰的 ByteBuf 对象。
|
||
|
||
- 每个实现方法,是对 `buf` 的对应方法的调用。例如:
|
||
|
||
```
|
||
@Override
|
||
public final int capacity() {
|
||
return buf.capacity();
|
||
}
|
||
|
||
@Override
|
||
public ByteBuf capacity(int newCapacity) {
|
||
buf.capacity(newCapacity);
|
||
return this;
|
||
}
|
||
```
|
||
|
||
# 7. SwappedByteBuf
|
||
|
||
`io.netty.buffer.SwappedByteBuf` ,继承 ByteBuf 抽象类,用于构建具有切换**字节序**功能的 ByteBuf 对象。构造方法如下:
|
||
|
||
```
|
||
/**
|
||
* 原 ByteBuf 对象
|
||
*/
|
||
private final ByteBuf buf;
|
||
/**
|
||
* 字节序
|
||
*/
|
||
private final ByteOrder order;
|
||
|
||
public SwappedByteBuf(ByteBuf buf) {
|
||
if (buf == null) {
|
||
throw new NullPointerException("buf");
|
||
}
|
||
this.buf = buf;
|
||
// 初始化 order 属性
|
||
if (buf.order() == ByteOrder.BIG_ENDIAN) {
|
||
order = ByteOrder.LITTLE_ENDIAN;
|
||
} else {
|
||
order = ByteOrder.BIG_ENDIAN;
|
||
}
|
||
}
|
||
```
|
||
|
||
- `buf` 属性,原 ByteBuf 对象。
|
||
|
||
- `order` 属性,字节数。
|
||
|
||
- 实际上,SwappedByteBuf 可以看成一个特殊的 WrappedByteBuf 实现,所以它除了读写操作外的方法,都是对 `buf` 的对应方法的调用。
|
||
|
||
- `#capacity()` 方法,代码如下:
|
||
|
||
```
|
||
@Override
|
||
public int capacity() {
|
||
return buf.capacity();
|
||
}
|
||
```
|
||
|
||
- 直接调用 `buf` 的对应方法。
|
||
|
||
- `#setInt(int index, int value)` 方法,代码如下:
|
||
|
||
```
|
||
@Override
|
||
public ByteBuf setInt(int index, int value) {
|
||
buf.setInt(index, ByteBufUtil.swapInt(value));
|
||
return this;
|
||
}
|
||
|
||
// ByteBufUtil.java
|
||
/**
|
||
* Toggles the endianness of the specified 32-bit integer.
|
||
*/
|
||
public static int swapInt(int value) {
|
||
return Integer.reverseBytes(value);
|
||
}
|
||
```
|
||
|
||
- 先调用 `ByteBufUtil#swapInt(int value)` 方法,将 `value` 的值,转换成相反字节序的 Int 值。
|
||
- 后调用 `buf` 的对应方法。
|
||
|
||
通过 SwappedByteBuf 类,我们可以很方便的修改原 ByteBuf 对象的字节序,并且无需进行内存复制。但是反过来,一定要注意,这两者是**共享**的。
|
||
|
||
# 8. ReplayingDecoderByteBuf
|
||
|
||
`io.netty.handler.codec.ReplayingDecoderByteBuf` ,继承 ByteBuf 抽象类,用于构建在 IO 阻塞条件下实现无阻塞解码的特殊 ByteBuf对 象。当要读取的数据还未接收完全时,抛出异常,交由 ReplayingDecoder 处理。
|
||
|
||
细心的胖友,会看到 ReplayingDecoderByteBuf 是在 `codec` 模块,配合 ReplayingDecoder 使用。所以,本文暂时不会分享它,而是在 [《TODO 2000 ReplayingDecoderByteBuf》](https://svip.iocoder.cn/Netty/ByteBuf-1-1-ByteBuf-intro/) 中,详细解析。
|
||
|
||
# 666. 彩蛋
|
||
|
||
每逢开篇,内容就特别啰嗦,哈哈哈哈。
|
||
|
||
推荐阅读如下文章:
|
||
|
||
- AbeJeffrey [《深入研究Netty框架之ByteBuf功能原理及源码分析》](https://my.oschina.net/7001/blog/742236)
|
||
- [《Netty 学习笔记 —— ByteBuf 继承结构》](https://skyao.gitbooks.io/learning-netty/content/buffer/inheritance.html) |