ByteBuf的I/O主要解决的问题有两个:

  • 管理readerIndex和writerIndex。这个在在AbstractByteBuf中解决。
  • 从内存中读写数据。ByteBuf的不同实现主要使用两种内存:堆内存表示为byte[];直接内,可能是DirectByteBuffer或者是一块裸内存。这个问题在HeapByteBufUtil, UnsafeByteBufUtil中解决。

管理readerIndex和writerIndex

    AbstractByteBuf中实现了对readerIndex和writerIndex的管理。下面以int数据的读写为例说明这两个属性的管理机制。

readerIndex

@Override public int readInt() {     checkReadableBytes0(4);     int v = _getInt(readerIndex);     readerIndex += 4;     return v; }

这个方法是从ByteBuf中读取一个int数据.

  • 第一步: checkReadableBytes0(4), 确保ByteBuf中有至少4个Byte的数据。
  • 第二步: _getInt从readerIndex指定的位置读取4个Byte的数据,并反序列化成一个int。
  • 第三步: readerIndex的值增加4。

    还有一个getInt方法,它也能从ByteBuf读出一个int数据,不同的是这个方法需要指定读取位置,也不会影响readerIndex的值。
    如果有需要,还能使用readerIndex(int readerIndex)方法,设置readerIndex的值。
    可以调用markedReaderIndex()把当前的readerIndex值保存到markedReaderIndex属性。在有需要的时候可以调用resetReaderIndex()把markedReaderIndex属性的值恢复到readerIndex。

writerIndex

@Override public ByteBuf writeInt(int value) {     ensureWritable0(4);     _setInt(writerIndex, value);     writerIndex += 4;     return this; }

     这个方法是向ByteBuf中写入一个int数据。

  • 第一步: ensureWritable0(4), 检查确保ByteBuf中有4个Byte的可写空间。
  • 第二步: _setInt把int数据写入到writerIndex指定的位置,写之前会把int数据方序列化成4个Byte的数据。
  • 第三步: writerIndex值增加4。

     setInt方法向ByteBuf写入一个int数据,不影响writerIndex。
     markWriterIndex和resetWriterIndex分别用来标记和恢复writerIndex。

     不同数据类型对readerIndex和writerIndex影响取决于数据的长度。每种数据类型的长度在上一章中有详细描述。 Bytes数据相对要复杂一些,后面会详述。

AbstractByteBuf定义的抽象的接口

    AbstractByteBuf这个抽象类主要实现对readerIndex和writerIndex的管理。它把真正的I/O操实现留给子类。
    以int类型的数据读写为例,它定义了两个抽象接口分别从指定位置读写数据: _getInt(int index), _setInt(int index, int value)。此外还有另外4组抽象方法,用来实现不同类型数据的读写:

  • _getByte, setByte
  • _getShort, setShort
  • _getUnsignedMedium, _setMedium
  • _getLong, _getLong

浮点数据的I/O

    浮点数是IEEE定义的标准内存内存结构,较整数要复杂一些。AbstractByteBuf使用int的I/O方法处理float数据,使用long的I/O方法处理double数据。下面是writeDouble的实现:

 @Override  public double readDouble() {      return Double.longBitsToDouble(readLong());  } @Override public ByteBuf writeDouble(double value) {     writeLong(Double.doubleToRawLongBits(value));     return this; }

    读的时候是先读出一个long数据,然后调用longBitsToDouble把long转换成double。写的时候先调用doubletoRawLongBits把double转换成long, 然后写入long数据。
    对float数据的处理类似,这里就不再赘述。

Bytes数据的I/O

     ByteBuf定义了5中不同类型的Bytes: byte[], ByteBuffer, InputStream, OutputStream, ScatteringByteChannel。其中byte[]和ByteBuffer有最大长度限制,读写的时候可以指定长度,也可以不指定。另外3种长度不确定,必须要指定长度,下面以byte[]为例看一下Bytes I/O的实现。

Bytes读

@Override public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {     checkReadableBytes(length);     getBytes(readerIndex, dst, dstIndex, length);     readerIndex += length;     return this; }  @Override public ByteBuf readBytes(byte[] dst) {     readBytes(dst, 0, dst.length);     return this; }

readBytes(dst)方法没有指定长度默认会读取dst最大长度的的数据。
readBytes(dst, dstIndex, length)方法指定的dst的起始位置和长度,把读取的数据放到dst的dst[dstIndex]到dst[dstIndex+length]区间内。同时他还负责管理readerIndex。
getBytes(index, dst, dstIndex, length)方法是功能最强大的方法,留给子类实现。

Bytes写