略解ByteBuf

說到ByteBuf,我們並不陌生,官網給的解釋爲,一個可以進行隨機訪問或者是順序訪問的字節集合,它是NIO buffers緩衝的底層抽象。既然是底層抽象,那麼我們就可以基於其衍生出很多的具體實現出來,事實上,netty中的很多緩衝組件都是基於此抽象類做的擴展。

隨機訪問索引

和普通的字節數據一樣,ByteBuf也是從0開始索引的。這就意味着第一個字節的索引永遠是0,而最後一個字節的索引則是capacity。舉個例子,當我們去遍歷緩衝中的所有字節的時候,我們可以按照如下方式來做:

 ByteBuf buffer = ...;
 for (int i = 0; i < buffer.capacity(); i ++) {
     byte b = buffer.getByte(i);
     System.out.println((char) b);
 }

可以清楚的看到,其使用方式和遍歷字節數組一樣的做法。我們可以隨機的獲取緩衝區裏面的任意一個字節。

順序訪問索引

這個ByteBuf的實現中,有三個比較有意思的屬性,readerIndex,writerIndex,capacity,從字面意思上,我們可以理解爲讀索引,寫索引,容量。下圖則展示了三個屬性之間的關係:

image

首先是readable bytes(可讀字節數組),裏面放置的是真實的數據,當使用帶有read或者是skip的方法來操作此數據內容的時候,都將導致readerIndex遞增。如果當前內容讀取完畢,沒有更多的內容可以讀取,那麼嘗試讀取將會拋出IndexOutOfBoundsException。默認情況下,一個新分配的緩衝區或者包裝的緩衝區或者複製的緩衝區,其readerIndex的初始值爲0。示例讀取代碼如下:

// Iterates the readable bytes of a buffer.
 ByteBuf buffer = ...;
 while (buffer.isReadable()) {
     System.out.println(buffer.readByte());
 }

其次是writable bytes(可寫字節數組),裏面是空數據,待被真實數據覆蓋。當使用帶有write的方法來操作此數據內容的時候,都將導致writerIndex遞增。如果當前已無足夠的空間可寫,那麼嘗試寫入將會拋出IndexOutOfBoundsException。默認情況下,一個新分配的緩存區,其writerIndex的初始值爲0。包裝的緩衝區或者複製的緩衝區,其writerIndex等於capacity。示例寫入代碼如下:

 // Fills the writable bytes of a buffer with random integers.
 ByteBuf buffer = ...;
 while (buffer.maxWritableBytes() >= 4) {
     buffer.writeInt(random.nextInt());
 }
 

最後是discardable bytes(廢棄字節數組),此數組裏面是已經讀取過的數據。開始的時候,其值默認爲0,但是當進行讀取操作的時候,它的值開始慢慢遞增,直至和writerIndex相等。這些字節可以通過調用discardReadBytes()方法來進行釋放,釋放前和釋放後的圖示示例如下:

釋放前:

image

釋放後:

image

需要注意的是,不同緩衝區的底層實現,可能會讓writable bytes裏面填充進完全不同的數據,所以使用此方法的時候,還請審慎。

你可以調用clear()方法來重置readerIndex和writerIndex爲0。此方法不會清理掉真實的數據,而是僅僅重置這兩個索引。

清理前:

image

清理後:

image

需要注意的是,此種情況下可能會覆蓋原有緩衝數據,使用的事情請謹慎。

搜索操作

簡單的單個字節搜索,可以使用indexOf(int, int, byte)bytesBefore(int, int, byte)來實現。bytesBefore(byte) 適用於簡單的String搜索。  forEachByte(int, int, ByteBufProcessor) 適用於比較複雜的搜索。

創建緩衝副本

你可以使用duplicate(), slice() 或者 slice(int, int)來創建已有緩衝的衍生副本。衍生的緩衝副本將會有獨立的readerIndex,writerIndex和標記索引,但是如同其他NIO 緩衝區一樣,他會共享內部的數據。爲了能夠獲得一份真正的全新的緩衝拷貝,可以使用copy()方法來進行。需要注意的是,衍生的緩衝將不會調用retain()方法,因爲其reference count將不會增加。

JDK類型轉換

你可以使用array()方法來使ByteBuf支持字節數組(比如 byte[])。同時也可以使用hasArray()方法來檢測其是否支持字節數組。

你可以使用nioBuffer()方法來使ByteBuf支持NIO ByteBuffer。同時也可以使用nioBufferCount()方法來檢測其是否能夠被轉換爲NIO buffer。

你可以使用toString(Charset)方法來使ByteBuf轉換成String。需要注意的是,toString()方法並非類型轉換的方法。

你可以使用ByteBufInputStream和ByteBufOutputStream來進行IO流的轉換。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章