Netty源碼_ByteBuf詳解

在分析 Netty 框架的時(shí)候么伯,我們首先介紹 ByteBuf 相關(guān)類罚随,因?yàn)榫W(wǎng)絡(luò)傳輸最終都是字節(jié)數(shù)據(jù)禁荒,所以如何管理字節(jié)數(shù)據(jù)是非常重要的海渊。
我們知道在Java NIO 編程中提供了 ByteBuffer 作為字節(jié)容器痊班,但是這個(gè)類使用起來過于復(fù)雜勤婚,而且極其容易犯錯(cuò),因?yàn)樗挥幸粋€(gè)索引記錄數(shù)據(jù)位置涤伐,導(dǎo)致讀寫操作還需要使用 flip 方法進(jìn)行切換蛔六,并且容量是固定的荆永,需要使用者手動(dòng)擴(kuò)容。
因此 Netty 提供了 ByteBuf 類來彌補(bǔ) ByteBuffer 的不足和缺陷国章。

一. 類型

1.1 內(nèi)存分配

ByteBuf 按照內(nèi)存分配劃分具钥,可以分為兩種:

  1. 堆緩沖區(qū): 即內(nèi)部使用 byte[] 字節(jié)數(shù)組存儲(chǔ)字節(jié)數(shù)據(jù)。
    • 它的優(yōu)點(diǎn)是字節(jié)數(shù)組可以快速的分配和釋放液兽,也可以被 JVM 自動(dòng)回收骂删;
    • 缺點(diǎn)是進(jìn)行 I/O 操作時(shí),需要進(jìn)行額外一次內(nèi)存復(fù)制四啰,將堆緩沖區(qū)數(shù)據(jù)復(fù)制到內(nèi)核中宁玫。
    • 堆緩存區(qū)的 ByteBuf 類名中都包含 Heap
  2. 直接緩沖區(qū): 即內(nèi)部使用 Java NIODirectByteBuffer 來存儲(chǔ)字節(jié)數(shù)據(jù)柑晒。
    • 它的優(yōu)點(diǎn)是避免在每次調(diào)用 I/O 操作之前(或者之后)將緩沖區(qū)的內(nèi)容復(fù)制到一個(gè)中間緩沖區(qū)(或者從中間緩沖區(qū)把內(nèi)容復(fù)制到緩沖區(qū))欧瘪。
    • 缺點(diǎn)是內(nèi)存分配和釋放都較為昂貴。
    • 直接緩沖區(qū)的 ByteBuf 類名中都包含 Direct 匙赞。

1.2 內(nèi)存回收

ByteBuf 按照內(nèi)存回收角度劃分佛掖,可以分為兩種:

  1. 池中緩存區(qū):維護(hù)一個(gè)內(nèi)存池,所有的緩存區(qū) ByteBuf 的內(nèi)存都來自于這個(gè)內(nèi)存池涌庭,可以重復(fù)使用內(nèi)存芥被,提高內(nèi)存使用效率,降低高負(fù)載下頻繁的 GC坐榆。

    注意不管是堆緩沖區(qū)還是直接緩沖區(qū)都可以使用內(nèi)存池拴魄。池中緩存區(qū)的 ByteBuf 類名中都包含 Pooled

  2. 非池中緩存區(qū):不使用內(nèi)存池席镀,直接創(chuàng)建內(nèi)存匹中;對(duì)于堆緩沖區(qū),就是直接創(chuàng)建 byte[] 字節(jié)數(shù)組豪诲,對(duì)于直接緩沖區(qū)顶捷,就是創(chuàng)建 DirectByteBuffer對(duì)象實(shí)例。

    注意不管是堆緩沖區(qū)還是直接緩沖區(qū)都可以是非池中緩存區(qū)跛溉。非池中緩存區(qū)的 ByteBuf 類名中都包含 Unpooled

1.3 Unsafe

最后 JDK 中是否包含 sun.misc.Unsafe 類扮授,緩存區(qū) ByteBuf 又分為安全的和非安全的芳室。使用 sun.misc.Unsafe 可以加快數(shù)據(jù)的訪問速度。

1.4 小結(jié)

因此這三種類型進(jìn)行排列組合刹勃,因此重要的緩存區(qū) ByteBuf一共分為八種類型:

  1. UnpooledHeapByteBuf
  2. UnpooledUnsafeHeapByteBuf
  3. UnpooledDirectByteBuf
  4. UnpooledUnsafeDirectByteBuf
  5. PooledHeapByteBuf
  6. PooledUnsafeHeapByteBuf
  7. PooledDirectByteBuf
  8. PooledUnsafeDirectByteBuf

這些類我們?cè)诤竺娴奈恼聲?huì)一一介紹堪侯,但是這些類型只是緩存區(qū) ByteBuf 的內(nèi)存不同,但是對(duì)于一般使用者來說荔仁,其實(shí)不用區(qū)分這么多伍宦,我們只需要知道如何使用緩存區(qū) ByteBuf 就可以了芽死。

二. 介紹

對(duì)于如何使用 ByteBuf ,我們直接看 ByteBuf類的文檔注釋

2.1 文檔注釋

A random and sequential accessible sequence of zero or more bytes (octets). This interface provides an abstract view for one or more primitive byte arrays (byte[]) and NIO buffers.
Creation of a buffer
It is recommended to create a new buffer using the helper methods in Unpooled rather than calling an individual implementation's constructor.
Random Access Indexing
Just like an ordinary primitive byte array, ByteBuf uses zero-based indexing . It means the index of the first byte is always 0 and the index of the last byte is always capacity - 1. For example, to iterate all bytes of a buffer, you can do the following, regardless of its internal implementation:
   ByteBuf buffer = ...;
   for (int i = 0; i < buffer.capacity(); i ++) {
       byte b = buffer.getByte(i);
       System.out.println((char) b);
   }
   
Sequential Access Indexing
ByteBuf provides two pointer variables to support sequential read and write operations - readerIndex for a read operation and writerIndex for a write operation respectively. The following diagram shows how a buffer is segmented into three areas by the two pointers:
        +-------------------+------------------+------------------+
        | discardable bytes |  readable bytes  |  writable bytes  |
        |                   |     (CONTENT)    |                  |
        +-------------------+------------------+------------------+
        |                   |                  |                  |
        0      <=      readerIndex   <=   writerIndex    <=    capacity
   
Readable bytes (the actual content)
This segment is where the actual data is stored. Any operation whose name starts with read or skip will get or skip the data at the current readerIndex and increase it by the number of read bytes. If the argument of the read operation is also a ByteBuf and no destination index is specified, the specified buffer's writerIndex is increased together.
If there's not enough content left, IndexOutOfBoundsException is raised. The default value of newly allocated, wrapped or copied buffer's readerIndex is 0.
   // Iterates the readable bytes of a buffer.
   ByteBuf buffer = ...;
   while (buffer.isReadable()) {
       System.out.println(buffer.readByte());
   }
   
Writable bytes
This segment is a undefined space which needs to be filled. Any operation whose name starts with write will write the data at the current writerIndex and increase it by the number of written bytes. If the argument of the write operation is also a ByteBuf, and no source index is specified, the specified buffer's readerIndex is increased together.
If there's not enough writable bytes left, IndexOutOfBoundsException is raised. The default value of newly allocated buffer's writerIndex is 0. The default value of wrapped or copied buffer's writerIndex is the capacity of the buffer.
   // Fills the writable bytes of a buffer with random integers.
   ByteBuf buffer = ...;
   while (buffer.maxWritableBytes() >= 4) {
       buffer.writeInt(random.nextInt());
   }
Discardable bytes
This segment contains the bytes which were read already by a read operation. Initially, the size of this segment is 0, but its size increases up to the writerIndex as read operations are executed. The read bytes can be discarded by calling discardReadBytes() to reclaim unused area as depicted by the following diagram:
    BEFORE discardReadBytes()
  
        +-------------------+------------------+------------------+
        | discardable bytes |  readable bytes  |  writable bytes  |
        +-------------------+------------------+------------------+
        |                   |                  |                  |
        0      <=      readerIndex   <=   writerIndex    <=    capacity
  
  
    AFTER discardReadBytes()
  
        +------------------+--------------------------------------+
        |  readable bytes  |    writable bytes (got more space)   |
        +------------------+--------------------------------------+
        |                  |                                      |
   readerIndex (0) <= writerIndex (decreased)        <=        capacity
   
Please note that there is no guarantee about the content of writable bytes after calling discardReadBytes(). The writable bytes will not be moved in most cases and could even be filled with completely different data depending on the underlying buffer implementation.
Clearing the buffer indexes
You can set both readerIndex and writerIndex to 0 by calling clear(). It does not clear the buffer content (e.g. filling with 0) but just clears the two pointers. Please also note that the semantic of this operation is different from ByteBuffer.clear().
 BEFORE clear()
  
        +-------------------+------------------+------------------+
        | discardable bytes |  readable bytes  |  writable bytes  |
        +-------------------+------------------+------------------+
        |                   |                  |                  |
        0      <=      readerIndex   <=   writerIndex    <=    capacity
  
  
    AFTER clear()
  
        +---------------------------------------------------------+
        |             writable bytes (got more space)             |
        +---------------------------------------------------------+
        |                                                         |
        0 = readerIndex = writerIndex            <=            capacity
   
Search operations
For simple single-byte searches, use indexOf(int, int, byte) and bytesBefore(int, int, byte). bytesBefore(byte) is especially useful when you deal with a NUL-terminated string. For complicated searches, use forEachByte(int, int, ByteProcessor) with a ByteProcessor implementation.
Mark and reset
There are two marker indexes in every buffer. One is for storing readerIndex and the other is for storing writerIndex. You can always reposition one of the two indexes by calling a reset method. It works in a similar fashion to the mark and reset methods in InputStream except that there's no readlimit.
Derived buffers
You can create a view of an existing buffer by calling one of the following methods:
· duplicate()
· slice()
· slice(int, int)
· readSlice(int)
· retainedDuplicate()
· retainedSlice()
· retainedSlice(int, int)
· readRetainedSlice(int)
A derived buffer will have an independent readerIndex, writerIndex and marker indexes, while it shares other internal data representation, just like a NIO buffer does.
In case a completely fresh copy of an existing buffer is required, please call copy() method instead.
Non-retained and retained derived buffers
Note that the duplicate(), slice(), slice(int, int) and readSlice(int) does NOT call retain() on the returned derived buffer, and thus its reference count will NOT be increased. If you need to create a derived buffer with increased reference count, consider using retainedDuplicate(), retainedSlice(), retainedSlice(int, int) and readRetainedSlice(int) which may return a buffer implementation that produces less garbage.
Conversion to existing JDK types
Byte array
If a ByteBuf is backed by a byte array (i.e. byte[]), you can access it directly via the array() method. To determine if a buffer is backed by a byte array, hasArray() should be used.
NIO Buffers
If a ByteBuf can be converted into an NIO ByteBuffer which shares its content (i.e. view buffer), you can get it via the nioBuffer() method. To determine if a buffer can be converted into an NIO buffer, use nioBufferCount().
Strings
Various toString(Charset) methods convert a ByteBuf into a String. Please note that toString() is not a conversion method.

這個(gè)文檔注釋很好地說明緩存區(qū) ByteBuf 作用和特性次洼。

2.2 緩存區(qū) ByteBuf 創(chuàng)建

創(chuàng)建緩存區(qū) ByteBuf 的時(shí)候关贵,推薦使用 Unpooled 類的工具方法,而不是直接調(diào)用它的構(gòu)造方法 new 出來卖毁。當(dāng)然也可以通過 ByteBufAllocator 實(shí)例創(chuàng)建緩存區(qū) ByteBuf對(duì)象揖曾。

2.3 隨機(jī)訪問索引

如同在普通的 Java 字節(jié)數(shù)組中一樣,ByteBuf 的索引是從零開始的:第一個(gè)字節(jié)的索引是0亥啦,最后一個(gè)字節(jié)的索引總是 capacity() - 1炭剪。因此你可以直接遍歷緩存區(qū) ByteBuf 數(shù)據(jù),而不用管它具體是那種實(shí)現(xiàn)翔脱。

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

2.4 順序訪問索引

緩存區(qū) ByteBuf 提供了兩個(gè)指針?biāo)饕齺碇С猪樞蜃x寫操作: readerIndex 用于讀操作奴拦,writerIndex 用于寫操作,因此這兩個(gè)索引將緩存區(qū)分成三個(gè)區(qū)域:

        +-------------------+------------------+------------------+
        | discardable bytes |  readable bytes  |  writable bytes  |
        |                   |     (CONTENT)    |                  |
        +-------------------+------------------+------------------+
        |                   |                  |                  |
        0      <=      readerIndex   <=   writerIndex    <=    capacity

2.4.1 Readable bytes (真實(shí)數(shù)據(jù))

這個(gè)片段是存儲(chǔ)實(shí)際數(shù)據(jù)的地方。任何名稱以 readskip 開頭的操作將獲得或跳過當(dāng)前 readerIndex 處的數(shù)據(jù)届吁,并在 readerIndex 上增加讀字節(jié)數(shù)错妖。新分配、包裝或復(fù)制的緩沖區(qū)的 readerIndex 的默認(rèn)值是 0瓷产。

  • 如果讀操作的參數(shù)也是一個(gè) ByteBuf 并且沒有指定目標(biāo)索引站玄,那么這個(gè)參數(shù)緩沖區(qū) 的writerIndex 將一起增加。例如 readBytes(ByteBuf dst)
  • 如果緩存區(qū)沒有足夠的可讀內(nèi)容濒旦,將引發(fā)IndexOutOfBoundsException株旷。
// Iterates the readable bytes of a buffer.
ByteBuf buffer = ...;
while (buffer.isReadable()) {
      System.out.println(buffer.readByte());
}

2.4.2 Writable bytes

這個(gè)片段是一個(gè)未定義的空間,需要被填充尔邓。任何名稱以 write 開頭的操作都將在當(dāng)前 writerIndex 處的寫入數(shù)據(jù)晾剖,并在 writerIndex 上增加寫入的字節(jié)數(shù)。新分配的緩沖區(qū)的 writerIndex 的默認(rèn)值是 0梯嗽。包裝或復(fù)制緩沖區(qū)的 writerIndex 的默認(rèn)值是緩沖區(qū)的容量值齿尽。

  • 如果寫操作的參數(shù)也是一個(gè) ByteBuf,并且沒有指定源索引灯节,則指定參數(shù)緩沖區(qū)的 readerIndex 將一起增加循头。例如 writeBytes(ByteBuf src)
  • 如果沒有足夠的可寫字節(jié),則會(huì)引發(fā)IndexOutOfBoundsException炎疆,即超過緩存區(qū)最大容量卡骂。
// Fills the writable bytes of a buffer with random integers.
ByteBuf buffer = ...;
while (buffer.maxWritableBytes() >= 4) {
      buffer.writeInt(random.nextInt());
}

2.4.3 Discardable bytes

這個(gè)片段包含已經(jīng)被讀操作讀取的字節(jié)。最初這個(gè)段的大小是0形入,但是隨著讀取操作的執(zhí)行全跨,它的大小增加到writerIndex∫谒欤可以通過調(diào)用 discardReadBytes() 來回收這個(gè)片段浓若。
調(diào)用 discardReadBytes() 之前:

        +-------------------+------------------+------------------+
        | discardable bytes |  readable bytes  |  writable bytes  |
        +-------------------+------------------+------------------+
        |                   |                  |                  |
        0      <=      readerIndex   <=   writerIndex    <=    capacity

調(diào)用 discardReadBytes() 之后:

        +------------------+--------------------------------------+
        |  readable bytes  |    writable bytes (got more space)   |
        +------------------+--------------------------------------+
        |                  |                                      |
   readerIndex (0) <= writerIndex (decreased)        <=        capacity
  • 請(qǐng)注意渺杉,在調(diào)用discardReadBytes()后,不能保證可寫字節(jié)的內(nèi)容挪钓。在大多數(shù)情況下是越,可寫字節(jié)不會(huì)被移動(dòng),甚至可以由完全不同的數(shù)據(jù)填充诵原,這取決于底層緩沖區(qū)的實(shí)現(xiàn)英妓。
  • 雖然你可能會(huì)傾向于頻繁地調(diào)用 discardReadBytes() 方法以確保可寫分段的最大化绍赛,但是請(qǐng)注意蔓纠,這將極有可能會(huì)導(dǎo)致內(nèi)存復(fù)制,因?yàn)榭勺x字節(jié)(圖中標(biāo)記為 readable bytes 的部分)必須被移動(dòng)到緩沖區(qū)的開始位置吗蚌。我們建議只在有真正需要的時(shí)候才這樣做腿倚,例如,當(dāng)內(nèi)存非常寶貴的時(shí)候蚯妇。

2.4.4 Clearing the buffer indexes

可以通過調(diào)用 clear()readerIndexwriterIndex 都設(shè)置為0敷燎。它不清除緩沖區(qū)內(nèi)容(例如用0填充),只是清除兩個(gè)指針箩言。還請(qǐng)注意硬贯,此操作的語義與ByteBuffer.clear()不同。

調(diào)用 clear() 之前:

        +-------------------+------------------+------------------+
        | discardable bytes |  readable bytes  |  writable bytes  |
        +-------------------+------------------+------------------+
        |                   |                  |                  |
        0      <=      readerIndex   <=   writerIndex    <=    capacity

調(diào)用 clear() 之后:

        +---------------------------------------------------------+
        |             writable bytes (got more space)             |
        +---------------------------------------------------------+
        |                                                         |
        0 = readerIndex = writerIndex            <=            capacity

緩存區(qū) ByteBuf 中的數(shù)據(jù)沒有變陨收,只不過索引 readerIndexwriterIndex 變成 0饭豹。

2.5 搜索操作

緩存區(qū) ByteBuf 提供從緩存區(qū)中搜索指定字節(jié)數(shù)據(jù)位置索引的方法。
對(duì)于簡(jiǎn)單的單字節(jié)搜索务漩,使用 indexOf(int, int, byte)bytesBefore(int, int, byte) 拄衰。bytesBefore(byte) 在處理以 null 結(jié)尾的字符串時(shí)特別有用。
對(duì)于復(fù)雜的搜索饵骨,使用帶有 ByteProcessor 實(shí)現(xiàn)的 forEachByte(int, int, ByteProcessor) 方法翘悉。

2.6 標(biāo)記和重置

每個(gè)緩沖區(qū)中都有兩個(gè)標(biāo)記索引。
一個(gè)用于存儲(chǔ)讀索引 readerIndex 居触,另一個(gè)用于存儲(chǔ)寫索引 writerIndex妖混。
您總是可以通過調(diào)用 reset 方法來重新定位這兩個(gè)索引中的一個(gè)。它的工作方式類似于 InputStream 中的標(biāo)記和重置方法轮洋,只是沒有讀取限制制市。

2.7 派生的緩沖區(qū)

派生緩沖區(qū)為 ByteBuf 提供了以專門的方式來呈現(xiàn)其內(nèi)容的視圖。這類視圖是通過以下方法被創(chuàng)建的:

  • duplicate()
  • slice()
  • slice(int, int)
  • readSlice(int)
  • retainedDuplicate()
  • retainedSlice()
  • retainedSlice(int, int)
  • readRetainedSlice(int)

派生緩沖區(qū)將有一個(gè)獨(dú)立的 readerIndex砖瞧、writerIndex 和標(biāo)記索引息堂,而它與 NIO 緩沖區(qū)一樣共享其他緩存區(qū)內(nèi)部數(shù)據(jù)嚷狞。

因此如果改變了共享緩存區(qū)內(nèi)部數(shù)據(jù)块促,那么派生緩沖區(qū)內(nèi)容也會(huì)跟著改變荣堰。所以如果你需要一個(gè)現(xiàn)有緩沖區(qū)的全新副本,請(qǐng)調(diào)用copy()方法竭翠。

非保留和保留的派生緩沖區(qū)

  • 注意: duplicate()振坚, slice(), slice(int, int)和readSlice(int)不會(huì)對(duì)返回的派生緩沖區(qū)調(diào)用retain()斋扰,因此它的引用計(jì)數(shù)不會(huì)增加渡八。
  • 如果你需要?jiǎng)?chuàng)建一個(gè)增加引用計(jì)數(shù)的派生緩沖區(qū),考慮使用retainedDuplicate()传货, retainedSlice()屎鳍, retainedSlice(int, int)和readRetainedSlice(int),這可能會(huì)返回一個(gè)產(chǎn)生更少垃圾的緩沖區(qū)實(shí)現(xiàn)问裕。

2.8 轉(zhuǎn)換到現(xiàn)有的 JDK 類型

2.8.1 字節(jié)數(shù)組

如果緩存區(qū) ByteBuf 使用字節(jié)數(shù)組(即 byte[] ) 儲(chǔ)存數(shù)據(jù)的逮壁,你可以通過 array() 方法直接訪問它。

要確定緩沖區(qū)是否由字節(jié)數(shù)組支持粮宛,應(yīng)該使用hasArray() 方法窥淆。

2.8.2 NIO 緩沖區(qū)

如果一個(gè)緩存區(qū) ByteBuf 可以轉(zhuǎn)換成一個(gè) NIO ByteBuffer,共享它的內(nèi)容(即視圖緩沖區(qū))巍杈,你可以通過 nioBuffer() 方法獲得它忧饭。

要確定緩沖區(qū)是否可以轉(zhuǎn)換為 NIO 緩沖區(qū),請(qǐng)使用 nioBufferCount() 方法筷畦。

2.8.3 字符串

各種toString(Charset)方法將一個(gè) ByteBuf轉(zhuǎn)換為一個(gè) String词裤。

請(qǐng)注意,toString()不是一個(gè)轉(zhuǎn)換方法, 它顯示當(dāng)前緩存區(qū)的變量信息的汁咏,而不是緩存區(qū)存儲(chǔ)內(nèi)容的文本表示亚斋。

2.8.4 I/O

通過 ByteBufInputStreamByteBufOutputStream 實(shí)現(xiàn)。

三. 方法

接下來我們分析 ByteBuf 的方法攘滩,弄懂了這些方法的作用帅刊,就可以不用管 ByteBuf 具體實(shí)現(xiàn)是什么,直接操作緩存區(qū) ByteBuf漂问。

3.1 get 系列方法

從指定索引位置讀取數(shù)據(jù)赖瞒,它有幾個(gè)特點(diǎn):

  • 使用 get 系列方法必須指定一個(gè)索引,也就是說它可以從任意位置讀取緩存區(qū)中的數(shù)據(jù)蚤假。只要讀取的數(shù)據(jù)不超出緩存區(qū)的范圍栏饮。
  • 它不會(huì)改變讀索引 readerIndex 和 寫索引 writerIndex 的值。

3.1.1 獲取基本數(shù)據(jù)類型的方法

  1. boolean getBoolean(int index): 獲取該緩沖區(qū)中指定絕對(duì)索引處的布爾值磷仰。
  2. byte getByte(int index): 獲取此緩沖區(qū)中位于指定絕對(duì)索引處的字節(jié)袍嬉。
  3. short getUnsignedByte(int index): 獲取該緩沖區(qū)中指定絕對(duì)索引處的無符號(hào)字節(jié)。注:返回值是 short 類型。
  4. short getShort(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的16位短整數(shù)伺通。
  5. short getShortLE(int index): 獲取該緩沖區(qū)中以小端字節(jié)順序指定的絕對(duì)索引處的16位短整數(shù)箍土。
  6. int getUnsignedShort(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的16位無符號(hào)短整數(shù)。注:返回值是 int 類型罐监。
  7. int getUnsignedShortLE(int index): 獲取該緩沖區(qū)中以小端字節(jié)順序指定的絕對(duì)索引處的無符號(hào)16位短整數(shù)吴藻。注:返回值是 int 類型。
  8. int getMedium(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的24位中整數(shù)弓柱。
  9. int getMediumLE(int index): 以小端字節(jié)順序在此緩沖區(qū)中指定的絕對(duì)索引處獲取一個(gè)24位中等整數(shù)沟堡。
  10. int getUnsignedMedium(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的無符號(hào)24位中等整數(shù)。
  11. int getUnsignedMediumLE(int index): 獲取該緩沖區(qū)中以小端字節(jié)順序指定的絕對(duì)索引處的無符號(hào)24位中等整數(shù)矢空。
  12. int getInt(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的32位整數(shù)航罗。
  13. int getIntLE(int index): 獲取此緩沖區(qū)中具有小端字節(jié)順序的指定絕對(duì)索引處的32位整數(shù)。
  14. long getUnsignedInt(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的無符號(hào)32位整數(shù)屁药。注:返回值是 long 類型伤哺。
  15. long getUnsignedIntLE(int index): 獲取此緩沖區(qū)中以小端字節(jié)順序指定絕對(duì)索引處的無符號(hào)32位整數(shù)。
  16. long getLong(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的64位長(zhǎng)整數(shù)者祖。
  17. long getLongLE(int index): 以小端字節(jié)順序在此緩沖區(qū)中指定的絕對(duì)索引處獲取一個(gè)64位長(zhǎng)整數(shù)立莉。
  18. char getChar(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的2字節(jié)UTF-16字符。
  19. float getFloat(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的32位浮點(diǎn)數(shù)七问。
  20. float getFloatLE(int index): 獲取該緩沖區(qū)中以小端字節(jié)順序指定的絕對(duì)索引處的32位浮點(diǎn)數(shù)蜓耻。
  21. double getDouble(int index): 獲取此緩沖區(qū)中指定絕對(duì)索引處的64位浮點(diǎn)數(shù)。
  22. double getDoubleLE(int index): 獲取該緩沖區(qū)中以小端字節(jié)順序指定的絕對(duì)索引處的64位浮點(diǎn)數(shù)械巡。

22 個(gè)方法讓使用者可以很輕松從緩存區(qū)中指定位置獲取想要基本類型數(shù)據(jù)刹淌。

3.1.2 和其他ByteBuf 交互

get 系列方法都是從該緩存區(qū)讀取數(shù)據(jù),與其他的緩存區(qū) ByteBuf 交互讥耗,就是將該緩存區(qū)傳輸?shù)狡渌彺鎱^(qū)中有勾。
一共有三個(gè)方法:

  1. ByteBuf getBytes(int index, ByteBuf dst)
  2. ByteBuf getBytes(int index, ByteBuf dst, int length)
  3. ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length)

這三個(gè)方法都是從指定的絕對(duì)索引開始,將該緩沖區(qū)的數(shù)據(jù)傳輸?shù)街付ǖ哪繕?biāo)緩存區(qū)dst 古程。
我們來看在 AbstractByteBuf 類中的基本實(shí)現(xiàn):

    @Override
    public ByteBuf getBytes(int index, ByteBuf dst) {
        getBytes(index, dst, dst.writableBytes());
        return this;
    }

    @Override
    public ByteBuf getBytes(int index, ByteBuf dst, int length) {
        getBytes(index, dst, dst.writerIndex(), length);
        dst.writerIndex(dst.writerIndex() + length);
        return this;
    }

我們看到:

  • ByteBuf getBytes(int index, ByteBuf dst) 方法通過直接調(diào)用 getBytes(int index, ByteBuf dst, int length) 方法來實(shí)現(xiàn)蔼卡,傳遞 length 大小就是目標(biāo)緩存區(qū) dst 的剩下可寫區(qū)域 Writable bytes 的大小。
  • getBytes(int index, ByteBuf dst, int length) 方法也調(diào)用了 ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) 方法挣磨,傳遞的dstIndex 就是目標(biāo)緩存區(qū) dst 當(dāng)前寫索引 writerIndex 值雇逞。
    但是最后它調(diào)用 dst.writerIndex() 方法,增加了目標(biāo)緩存區(qū) dst 的寫索引 writerIndex 值茁裙。

因此我們可以得出:

  • 首先這三個(gè)方法都不會(huì)改變?cè)摼彌_區(qū)的讀索引readerIndex或者寫索引writerIndex的值塘砸。
  • 前兩個(gè)方法會(huì)改變目標(biāo)緩存區(qū) dst 的寫索引 writerIndex 值,而第三個(gè)方法則不會(huì)晤锥。
    • 這是因?yàn)榍皟蓚€(gè)方法是將該緩沖區(qū)的數(shù)據(jù)從目標(biāo)緩存區(qū) dst 當(dāng)前寫索引 writerIndex 位置處開始寫入掉蔬,因此傳輸完之后廊宪,可以更改目標(biāo)緩存區(qū) dst 的寫索引 writerIndex
    • 而第三個(gè)方法則是從目標(biāo)緩存區(qū) dst 任意位置處開始寫入女轿,就不好直接改變寫索引 writerIndex了挤忙。

3.1.3 和字節(jié)數(shù)組交互

get 系列方法都是從該緩存區(qū)讀取數(shù)據(jù),與字節(jié)數(shù)組交互谈喳,就是將緩存區(qū)讀取到字節(jié)數(shù)組中。

  1. ByteBuf getBytes(int index, byte[] dst)
  2. ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length)

從指定的絕對(duì)索引開始戈泼,將該緩沖區(qū)的數(shù)據(jù)傳輸?shù)街付繕?biāo)字節(jié)數(shù)組dst婿禽。
第一個(gè)方法實(shí)現(xiàn)就是

    public ByteBuf getBytes(int index, byte[] dst) {
        getBytes(index, dst, 0, dst.length);
        return this;
    }

3.1.4 和 ByteBuffer 交互

將該緩存區(qū)數(shù)據(jù)讀取到 ByteBuffer 中去。
只有一個(gè)方法: ByteBuf getBytes(int index, ByteBuffer dst)
即從指定的絕對(duì)索引開始將緩沖區(qū)的數(shù)據(jù)傳輸?shù)街付ǖ哪繕?biāo) ByteBuffer大猛,直到目標(biāo) ByteBuffer 數(shù)據(jù)達(dá)到其限制扭倾,即ByteBuffer 已經(jīng)被寫滿了。

3.1.5 和 IO 流交互

  1. ByteBuf getBytes(int index, OutputStream out, int length): 從指定的絕對(duì)索引處開始將該緩沖區(qū)的數(shù)據(jù)傳輸?shù)街付ǖ牧鳌?/li>
  2. int getBytes(int index, GatheringByteChannel out, int length): 從指定的絕對(duì)索引處開始將緩沖區(qū)的數(shù)據(jù)傳輸?shù)街付ǖ耐ǖ馈?/li>
  3. int getBytes(int index, FileChannel out, long position, int length): 將從指定的絕對(duì)索引處開始的緩沖區(qū)數(shù)據(jù)傳輸?shù)綇慕o定文件位置開始的指定通道挽绩。

3.1.5 獲取 CharSequence 對(duì)象

CharSequence getCharSequence(int index, int length, Charset charset) 獲取在給定索引處具有給定長(zhǎng)度的 CharSequence膛壹。

3.2 set 系列方法

從指定索引位置設(shè)置數(shù)據(jù),它有幾個(gè)特點(diǎn):

  • 使用 set 系列方法必須指定一個(gè)索引唉堪,也就是說它可以從任意位置設(shè)置緩存區(qū)中的數(shù)據(jù)模聋。只要設(shè)置的數(shù)據(jù)不超出緩存區(qū)的范圍。
  • 它不會(huì)改變讀索引 readerIndex 和 寫索引 writerIndex 的值唠亚。

3.2.1 設(shè)置基本數(shù)據(jù)類型的方法

  1. ByteBuf setBoolean(int index, boolean value): 在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的布爾值链方。
  2. ByteBuf setByte(int index, int value): 在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的字節(jié)。指定值的24個(gè)高階位被忽略灶搜。
    3.ByteBuf setShort(int index, int value): 在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的16位短整數(shù)祟蚀。忽略指定值的16個(gè)高階位。
  3. ByteBuf setShortLE(int index, int value): 在此緩沖區(qū)中使用小端字節(jié)順序的指定絕對(duì)索引處設(shè)置指定的16位短整數(shù)割卖。忽略指定值的16個(gè)高階位前酿。
  4. ByteBuf setMedium(int index, int value): 在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的24位中等整數(shù)。忽略指定值的8個(gè)高階位鹏溯。
  5. ByteBuf setMediumLE(int index, int value): 以小端字節(jié)順序在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的24位中位數(shù)罢维。忽略指定值的8個(gè)高階位。
  6. ByteBuf setInt(int index, int value): 在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的32位整數(shù)丙挽。
  7. ByteBuf setIntLE(int index, int value): 以小端字節(jié)順序在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的32位整數(shù)言津。
  8. ByteBuf setLong(int index, long value): 在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的64位長(zhǎng)整數(shù)。
  9. ByteBuf setLongLE(int index, long value): 以小端字節(jié)順序在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的64位長(zhǎng)整數(shù)取试。
  10. ByteBuf setChar(int index, int value): 在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的2字節(jié)UTF-16字符悬槽。
  11. ByteBuf setFloat(int index, float value): 在此緩沖區(qū)中指定的絕對(duì)索引處設(shè)置指定的32位浮點(diǎn)數(shù)。
  12. setFloatLE(int index, float value): 以小端字節(jié)順序在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的32位浮點(diǎn)數(shù)瞬浓。
  13. ByteBuf setDouble(int index, double value): 在此緩沖區(qū)中指定的絕對(duì)索引處設(shè)置指定的64位浮點(diǎn)數(shù)初婆。
    15 ByteBuf setDoubleLE(int index, double value): 以小端字節(jié)順序在此緩沖區(qū)的指定絕對(duì)索引處設(shè)置指定的64位浮點(diǎn)數(shù)。

15 個(gè)方法讓使用者可以很輕松在緩存區(qū)中指定位置設(shè)置各種基本類型數(shù)據(jù)。set 系列方法比 get 少是因?yàn)闆]有 setUnsigned... 的方法磅叛。

3.2.2 和其他 ByteBuf 交互

set 系列方法都是向本緩存區(qū)設(shè)置數(shù)據(jù)屑咳,因此與其他的緩存區(qū) ByteBuf 交互,就是將其他緩存區(qū)的數(shù)據(jù)寫入到本緩存區(qū)弊琴。
一共有三個(gè)方法:

  1. ByteBuf setBytes(int index, ByteBuf src)
  2. ByteBuf setBytes(int index, ByteBuf src, int length)
  3. ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length)

這三個(gè)方法都是從指定的絕對(duì)索引處開始將指定源緩沖區(qū) src的數(shù)據(jù)傳輸?shù)酱司彌_區(qū)兆龙。
我們來看在 AbstractByteBuf 類中的基本實(shí)現(xiàn):

    @Override
    public ByteBuf setBytes(int index, ByteBuf src) {
        setBytes(index, src, src.readableBytes());
        return this;
    }
    @Override
    public ByteBuf setBytes(int index, ByteBuf src, int length) {
        checkIndex(index, length);
        ObjectUtil.checkNotNull(src, "src");
        if (checkBounds) {
            checkReadableBounds(src, length);
        }

        setBytes(index, src, src.readerIndex(), length);
        src.readerIndex(src.readerIndex() + length);
        return this;
    }

我們可以看到和 get 系列方法幾乎差不多:

  • ByteBuf setBytes(int index, ByteBuf src) 方法通過直接調(diào)用 ByteBuf setBytes(int index, ByteBuf src, int length) 方法來實(shí)現(xiàn),傳遞 length 大小就是源緩存區(qū)src 可讀區(qū)域 Readable Bytes 的大小敲董。
  • ByteBuf setBytes(int index, ByteBuf src, int length) 方法也調(diào)用了 ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) 方法紫皇,傳遞的 srcIndex 就是源緩存區(qū)src 的當(dāng)前讀索引 readerIndex 的值。
    但是最后它調(diào)用了 src.readerIndex() 方法腋寨,增加了源緩存區(qū)src 的讀索引 readerIndex 的值聪铺。

因此我們可以得出:

  • 首先這三個(gè)方法都不會(huì)改變本緩沖區(qū)的讀索引 readerIndex 或者寫索引 writerIndex 的值。
  • 前兩個(gè)方法會(huì)改變?cè)淳彺鎱^(qū)src 的讀索引 readerIndex 的值萄窜,而第三個(gè)方法則不會(huì)铃剔。

3.2.3 和字節(jié)數(shù)組交互

將字節(jié)數(shù)組中的數(shù)據(jù)寫入到本緩存區(qū)。

  1. ByteBuf setBytes(int index, byte[] src)
  2. ByteBuf setBytes(int index, byte[] src, int srcIndex, int length)
    從指定的絕對(duì)索引處開始將指定源字節(jié)數(shù)組的數(shù)據(jù)傳輸?shù)酱司彌_區(qū)查刻。
    @Override
    public ByteBuf setBytes(int index, byte[] src) {
        setBytes(index, src, 0, src.length);
        return this;
    }

3.2.4 和 ByteBuffer 交互

  1. ByteBuf setBytes(int index, ByteBuffer src)
    從指定的絕對(duì)索引處開始將指定源緩沖區(qū) src 的數(shù)據(jù)傳輸?shù)酱司彌_區(qū)键兜,直到源緩沖區(qū)src的位置達(dá)到其極限,即源緩沖區(qū)src數(shù)據(jù)被讀取完穗泵。

3.2.5 和 IO 流交互

  1. int setBytes(int index, InputStream in, int length) throws IOException : 從指定的絕對(duì)索引處開始將指定源流的內(nèi)容傳輸?shù)酱司彌_區(qū)蝶押。

    返回從指定通道讀入的實(shí)際字節(jié)數(shù)。如果是 -1 則指定的通道被關(guān)閉火欧。

  2. int setBytes(int index, ScatteringByteChannel in, int length) throws IOException: 從指定的絕對(duì)索引開始棋电,將指定源通道的內(nèi)容傳輸?shù)酱司彌_區(qū)。

    返回從指定通道讀入的實(shí)際字節(jié)數(shù)苇侵。如果是 -1 則指定的通道被關(guān)閉赶盔。

  3. int setBytes(int index, FileChannel in, long position, int length) throws IOException: 將從給定文件位置開始的指定源通道的內(nèi)容傳輸?shù)綇闹付ń^對(duì)索引開始的緩沖區(qū)。

    返回從指定通道讀入的實(shí)際字節(jié)數(shù)榆浓。如果是 -1 則指定的通道被關(guān)閉于未。

3.2.5 設(shè)置 CharSequence 對(duì)象

int setCharSequence(int index, CharSequence sequence, Charset charset): 在給定索引處設(shè)置 sequence 對(duì)象數(shù)據(jù),并返回設(shè)置的字節(jié)長(zhǎng)度陡鹃。

3.2.6 設(shè)置 NUL 的方法

ByteBuf setZero(int index, int length) :從指定的絕對(duì)索引開始烘浦,用NUL (0x00)填充此緩沖區(qū) length 長(zhǎng)度的數(shù)據(jù)。

3.3 read 系列方法

read 系列方法與 get 系列方法一模一樣萍鲸,作用也是一樣的闷叉,區(qū)別如下:

  • read 系列方法不用指定索引,只能從緩存區(qū)當(dāng)前讀索引 readerIndex 位置讀取數(shù)據(jù)脊阴。
  • read 系列方法讀取完數(shù)據(jù)之后握侧,都會(huì)改變當(dāng)前讀索引 readerIndex 的值蚯瞧。

還有一個(gè) ByteBuf skipBytes(int length) 方法是己,將該緩沖區(qū)中的當(dāng)前readerIndex 增加指定的長(zhǎng)度煌茴。

注意 read 系列 和 skipBytes 方法能改變讀索引 readerIndex 的值。
除了它們全陨,只剩下 readerIndex(int readerIndex), setIndex(int readerIndex, int writerIndex), clear() ,resetReaderIndex(), discardReadBytes(), discardSomeReadBytes()方法能改變讀索引萄传。

3.4 write 系列方法

write 系列方法與 set 系列方法一模一樣甚颂,作用也是一樣的,區(qū)別如下:

  • write 系列方法不用指定索引秀菱,只能從緩存區(qū)當(dāng)前寫索引 writerIndex 位置寫入數(shù)據(jù)振诬。
  • write 系列方法寫入數(shù)據(jù)之后,都會(huì)改變當(dāng)前寫索引 writerIndex 的值答朋。

注意 write 系列方法能改變寫索引 writerIndex 的值。
除了它們棠笑,只剩下 writerIndex(int writerIndex), setIndex(int readerIndex, int writerIndex), clear() , resetWriterIndex(), discardReadBytes(), discardSomeReadBytes()方法能改變寫索引 writerIndex 的值梦碗。

3.5 搜索

3.5.1 簡(jiǎn)單的單字節(jié)搜索

  1. int indexOf(int fromIndex, int toIndex, byte value): 定位指定字節(jié)值value在此緩沖區(qū)中的第一次出現(xiàn)的位置。如果找不到就返回 -1 蓖救。

    • 搜索從指定的fromIndex(inclusive)到指定的toIndex(exclusive)洪规。
    • 如果fromIndex大于toIndex,搜索將按照從fromIndex(exclusive)到toIndex (inclusive)的相反順序執(zhí)行循捺。
    • 請(qǐng)注意斩例,較低的索引總是被包含,較高的索引總是被排除从橘。
  2. int bytesBefore(byte value): 定位指定值在此緩沖區(qū)中的第一次出現(xiàn)念赶。搜索從當(dāng)前的讀索引readerIndex(inclusive)到當(dāng)前的寫索引writerIndex(exclusive)。如果找不到就返回 -1 恰力。

  3. int bytesBefore(int length, byte value): 定位指定值在此緩沖區(qū)中的第一次出現(xiàn)叉谜。搜索從當(dāng)前readerIndex(inclusive)開始,并持續(xù)指定的長(zhǎng)度踩萎。如果找不到就返回 -1 停局。

  4. int bytesBefore(int index, int length, byte value): 定位指定值在此緩沖區(qū)中的第一次出現(xiàn)。搜索從指定的索引index(inclusive)開始香府,并持續(xù)指定的長(zhǎng)度董栽。如果找不到就返回 -1

3.5.2 復(fù)雜搜索

  1. int forEachByte(ByteProcessor processor): 用指定的處理器按升序遍歷該緩沖區(qū)的可讀字節(jié)企孩。
    我們來看 forEachByte 方法的實(shí)現(xiàn):
        public int forEachByte(ByteProcessor processor) {
         ensureAccessible();
         try {
             return forEachByteAsc0(readerIndex, writerIndex, processor);
         } catch (Exception e) {
             PlatformDependent.throwException(e);
             return -1;
         }
      }
          int forEachByteAsc0(int start, int end, ByteProcessor processor) throws Exception {
         for (; start < end; ++start) {
             if (!processor.process(_getByte(start))) {
                 return start;
             }
         }
    
         return -1;
     }
    
    你會(huì)發(fā)現(xiàn)它的確按照升序遍歷該緩沖區(qū)的可讀字節(jié)锭碳,當(dāng) processorprocess 返回 false 即不通過的時(shí)候,這個(gè)字節(jié)就是我們需要找的字節(jié)勿璃,返回這個(gè)字節(jié)的索引位置工禾。如果遍歷完可讀字節(jié)运提, processorprocess 都返回 true ,表示找不到闻葵,那么返回 -1民泵。
  2. int forEachByte(int index, int length, ByteProcessor processor): 用指定的處理器按升序遍歷該緩沖區(qū)的指定區(qū)域(即 index(index + 1)槽畔, ..(index + length - 1))栈妆。
  3. int forEachByteDesc(ByteProcessor processor): 使用指定的處理器按降序遍歷該緩沖區(qū)的可讀字節(jié)。
  4. int forEachByteDesc(int index, int length, ByteProcessor processor): 使用指定的處理器按降序遍歷該緩沖區(qū)的指定區(qū)域厢钧。(即(index + length - 1), (index + length - 2)鳞尔,…index)。

3.6 復(fù)制緩存區(qū)

  1. ByteBuf copy(): 返回該緩沖區(qū)的可讀字節(jié)的副本早直。修改返回的緩沖區(qū)或該緩沖區(qū)的內(nèi)容根本不會(huì)影響彼此寥假。這個(gè)方法與buf.copy(buf.readerIndex(), buf.readableBytes()) 相同霞扬。
  2. ByteBuf copy(int index, int length): 返回該緩沖區(qū)的子區(qū)域的副本糕韧。修改返回的緩沖區(qū)或該緩沖區(qū)的內(nèi)容根本不會(huì)影響彼此。

copy 創(chuàng)建新的內(nèi)存來存儲(chǔ)源緩存區(qū)的內(nèi)容喻圃,因此它們的內(nèi)容不會(huì)影響彼此萤彩。

3.7 派生的緩沖區(qū)

  1. ByteBuf slice(): 返回該緩沖區(qū)可讀字節(jié)的一個(gè)切片。

    修改返回緩沖區(qū)或此緩沖區(qū)的內(nèi)容會(huì)影響彼此的內(nèi)容斧拍,同時(shí)它們維護(hù)單獨(dú)的索引和標(biāo)記雀扶。這個(gè)方法與 buf.slice(buf.readerIndex(), buf.readableBytes()) 相同肆汹。

  2. ByteBuf retainedSlice(): 返回該緩沖區(qū)可讀字節(jié)的保留片愚墓。
    • 修改返回緩沖區(qū)或此緩沖區(qū)的內(nèi)容會(huì)影響彼此的內(nèi)容,同時(shí)它們維護(hù)單獨(dú)的索引和標(biāo)記昂勉。這個(gè)方法與buf.slice(buf.readerIndex()转绷, buf.readableBytes())相同。
    • slice()不同硼啤,此方法返回一個(gè)保留的緩沖區(qū)议经。這個(gè)方法的行為類似于slice().retain(),除了這個(gè)方法可能返回產(chǎn)生更少垃圾的緩沖區(qū)實(shí)現(xiàn)谴返。
  3. ByteBuf slice(int index, int length): 返回這個(gè)緩沖區(qū)的子區(qū)域的一個(gè)切片煞肾。
    • 修改返回緩沖區(qū)或此緩沖區(qū)的內(nèi)容會(huì)影響彼此的內(nèi)容,同時(shí)它們維護(hù)單獨(dú)的索引和標(biāo)記嗓袱。
    • 這個(gè)方法不會(huì)調(diào)用retain()籍救,因此引用計(jì)數(shù)不會(huì)增加。
  4. ByteBuf retainedSlice(int index, int length): 返回該緩沖區(qū)子區(qū)域的保留片渠抹。
    • 修改返回緩沖區(qū)或此緩沖區(qū)的內(nèi)容會(huì)影響彼此的內(nèi)容蝙昙,同時(shí)它們維護(hù)單獨(dú)的索引和標(biāo)記闪萄。
    • slice(int, int)不同,此方法返回一個(gè)保留的緩沖區(qū)奇颠。這個(gè)方法的行為類似于slice(…).retain()败去,除了這個(gè)方法可能返回產(chǎn)生更少垃圾的緩沖區(qū)實(shí)現(xiàn)。
  5. ByteBuf readSlice(int length): 返回該緩沖區(qū)子區(qū)域從當(dāng)前readerIndex處開始的一個(gè)新切片烈拒,并將readerIndex增加新切片的大小(length)圆裕。這個(gè)方法不會(huì)調(diào)用retain(),因此引用計(jì)數(shù)不會(huì)增加荆几。
    @Override
    public ByteBuf readSlice(int length) {
        checkReadableBytes(length);
        ByteBuf slice = slice(readerIndex, length);
        readerIndex += length;
        return slice;
    }
    
  6. ByteBuf readRetainedSlice(int length): 返回該緩沖區(qū)子區(qū)域從當(dāng)前readerIndex處開始的一個(gè)新的保留片吓妆,并增加readerIndex的大小(=length)。這個(gè)方法的行為類似于readSlice(…).retain()吨铸,除了這個(gè)方法可能返回一個(gè)產(chǎn)生更少垃圾的緩沖區(qū)實(shí)現(xiàn)行拢。
    @Override
    public ByteBuf readRetainedSlice(int length) {
        checkReadableBytes(length);
        ByteBuf slice = retainedSlice(readerIndex, length);
        readerIndex += length;
        return slice;
    }
    
  7. ByteBuf duplicate(): 返回一個(gè)共享該緩沖區(qū)的整個(gè)區(qū)域的緩沖區(qū)。
    • 修改返回緩沖區(qū)或此緩沖區(qū)的內(nèi)容會(huì)影響彼此的內(nèi)容诞吱,同時(shí)它們維護(hù)單獨(dú)的索引和標(biāo)記舟奠。讀索引標(biāo)記 markedReaderIndex和寫索引標(biāo)記markedWriterIndex不會(huì)被復(fù)制。
    • 這個(gè)方法不會(huì)調(diào)用retain()狐胎,因此引用計(jì)數(shù)不會(huì)增加鸭栖。
  8. ByteBuf retainedDuplicate(): 返回一個(gè)保留的緩沖區(qū)歌馍,該緩沖區(qū)共享該緩沖區(qū)的整個(gè)區(qū)域握巢。
    • 修改返回緩沖區(qū)或此緩沖區(qū)的內(nèi)容會(huì)影響彼此的內(nèi)容,同時(shí)它們維護(hù)單獨(dú)的索引和標(biāo)記松却。讀索引標(biāo)記 markedReaderIndex和寫索引標(biāo)記markedWriterIndex不會(huì)被復(fù)制暴浦。
    • 這個(gè)方法的行為類似于duplicate().retain(),除了這個(gè)方法可能返回產(chǎn)生更少垃圾的緩沖區(qū)實(shí)現(xiàn)晓锻。

3.8 轉(zhuǎn)換成 ByteBuffer

將此緩存區(qū)轉(zhuǎn)換成 Java NIO 的緩存區(qū) ByteBuffer 對(duì)象歌焦。

  1. int nioBufferCount(): 返回由該緩沖區(qū)組成的NIO ByteBuffers的最大數(shù)目。

    • 注意 nioBuffers()nioBuffers(int, int) 可能會(huì)返回較少數(shù)量的ByteBuffers砚哆。
    • 如果返回 -1 独撇,表示這個(gè)緩沖區(qū)沒有底層的NIO ByteBuffer
  2. ByteBuffer nioBuffer(): 將該緩沖區(qū)的可讀字節(jié)轉(zhuǎn)換成為單個(gè)NIO緩沖區(qū)躁锁,這個(gè)方法與buf.nioBuffer(buf.readerIndex(), buf.readableBytes())相同纷铣。

  3. ByteBuffer nioBuffer(int index, int length): 將該緩沖區(qū)的子區(qū)域轉(zhuǎn)換成為單個(gè)NIO緩沖區(qū)。

  4. ByteBuffer[] nioBuffers(): 將該緩沖區(qū)的可讀字節(jié)轉(zhuǎn)換成為NIO緩沖區(qū)數(shù)組战转,這個(gè)方法與buf.nioBuffers(buf.readerIndex(), buf.readableBytes())相同搜立。

  5. ByteBuffer[] nioBuffers(int index, int length):將該緩沖區(qū)的子區(qū)域轉(zhuǎn)換成為NIO緩沖區(qū)數(shù)組。

  • 上面四個(gè)方法將該緩沖區(qū)轉(zhuǎn)換成單個(gè)NIO緩沖區(qū)或者NIO緩沖區(qū)數(shù)組槐秧。而返回的NIO緩沖區(qū)共享該緩沖區(qū)內(nèi)容或者復(fù)制該緩沖區(qū)內(nèi)容啄踊,并且更改返回的NIO緩沖區(qū)的positionlimit不會(huì)影響該緩沖區(qū)的索引和標(biāo)記忧设。
  • 重點(diǎn)注意:如果這個(gè)緩沖區(qū)是動(dòng)態(tài)的,并且它調(diào)整了容量颠通,那么返回的NIO緩沖區(qū)將看不到這個(gè)緩沖區(qū)的變化址晕。

最后還有一個(gè)即僅內(nèi)部使用獲取NIO緩沖區(qū)方法 ByteBuffer internalNioBuffer(int index, int length)

3.9 轉(zhuǎn)換成字節(jié)數(shù)組

  1. boolean hasArray(): 當(dāng)且僅當(dāng)該緩沖區(qū)有支撐字節(jié)數(shù)組(backing byte array)時(shí)返回true蒜哀。

    如果這個(gè)方法返回true斩箫,您可以安全地調(diào)用array()arrayOffset()

  2. byte[] array(): 返回此緩沖區(qū)支撐字節(jié)數(shù)組(backing byte array)撵儿。
  3. int arrayOffset(): 返回此緩沖區(qū)的支撐字節(jié)數(shù)組(backing byte array)中第一個(gè)字節(jié)的偏移量乘客。
  4. boolean hasMemoryAddress(): 當(dāng)且僅當(dāng)該緩沖區(qū)使用底層內(nèi)存地址存儲(chǔ)數(shù)據(jù)時(shí)返回true。
  5. long memoryAddress(): 返回指向存儲(chǔ)的支撐數(shù)據(jù)的第一個(gè)字節(jié)的底層內(nèi)存地址淀歇。

3.10 轉(zhuǎn)換成字符串

  1. String toString(Charset charset): 將此緩沖區(qū)的可讀字節(jié)解碼為具有指定字符集名稱的字符串易核。這個(gè)方法與buf.toString(buf.readerIndex(), buf.readableBytes(), charsetName)相同。
  2. String toString(int index, int length, Charset charset): 將此緩沖區(qū)的子區(qū)域解碼為具有指定字符集的字符串浪默。

3.11 其他方法

  1. int capacity(): 返回該緩沖區(qū)可以包含的字節(jié)數(shù)牡直。

  2. ByteBuf capacity(int newCapacity): 調(diào)整此緩沖區(qū)的容量。

    • 如果newCapacity小于當(dāng)前容量纳决,則該緩沖區(qū)的內(nèi)容將被截?cái)唷?/li>
    • 如果newCapacity大于當(dāng)前容量碰逸,則在緩沖區(qū)中追加(newCapacity - currentCapacity)長(zhǎng)度的未指定數(shù)據(jù)。
  3. int maxCapacity(): 返回此緩沖區(qū)的最大允許容量阔加。該值提供了capacity()的上限饵史。

  4. ByteBufAllocator alloc(): 返回創(chuàng)建此緩沖區(qū)的ByteBufAllocator

  5. ByteBuf unwrap(): 如果該緩沖區(qū)是另一個(gè)緩沖區(qū)的包裝器胜榔,則返回底層緩沖區(qū)實(shí)例胳喷。

  6. boolean isDirect(): 當(dāng)且僅當(dāng)該緩沖區(qū)由NIO直接緩沖區(qū)支持時(shí)返回true。

  7. boolean isReadOnly(): 當(dāng)且僅當(dāng)此緩沖區(qū)是只讀緩沖區(qū)時(shí)返回true夭织。

  8. ByteBuf asReadOnly(): 返回此緩沖區(qū)的只讀版本吭露。

  9. int readerIndex(): 返回此緩存區(qū)的讀索引值。

  10. ByteBuf readerIndex(int readerIndex): 重新設(shè)置該緩沖區(qū)的讀索引readerIndex 值尊惰。

  11. int writerIndex(): 返回此緩存區(qū)的寫索引值讲竿。

  12. ByteBuf writerIndex(int writerIndex): 重新設(shè)置該緩沖區(qū)的寫索引writerIndex 值。

  13. ByteBuf setIndex(int readerIndex, int writerIndex): 同時(shí)設(shè)置該緩沖區(qū)的讀索引和寫索引的值弄屡。

  14. int readableBytes(): 返回該緩存區(qū)的可讀字節(jié)數(shù), 即(writerIndex -readerIndex)题禀。

  15. int writableBytes(): 返回該緩存區(qū)的可寫字節(jié)數(shù), 即(capacity - writerIndex)

  16. int maxWritableBytes(): 返回該緩存區(qū)的最大可寫字節(jié)數(shù), 即(maxCapacity - writerIndex)琢岩。

  17. int maxFastWritableBytes(): 返回可以確定寫入的最大字節(jié)數(shù)投剥,而不涉及內(nèi)部重新分配或數(shù)據(jù)拷貝。

    writableBytes() ≤ maxFastWritableBytes() ≤ maxWritableBytes()

  18. boolean isReadable(): 當(dāng)且僅當(dāng) writerIndex - readerIndex 的值大于0担孔。

  19. boolean isReadable(int size): 當(dāng)且僅當(dāng)該緩沖區(qū)包含等于或大于指定數(shù)量的元素時(shí)返回true江锨,即 writerIndex - readerIndex >= size吃警。

  20. boolean isWritable(): 當(dāng)且僅當(dāng)capacity() - writerIndex大于0

  21. boolean isWritable(int size): 當(dāng)且僅當(dāng)該緩沖區(qū)有足夠的空間允許寫入指定數(shù)量的元素時(shí)返回true啄育,即 capacity() - writerIndex >= size酌心。

  22. ByteBuf clear(): 將該緩沖區(qū)的readerIndexwriterIndex設(shè)置為0,這個(gè)方法與setIndex(0,0)相同挑豌。

  23. ByteBuf discardReadBytes(): 丟棄第0個(gè)索引和readerIndex之間的字節(jié)安券。它將readerIndexwriterIndex之間的字節(jié)移動(dòng)到第0個(gè)索引和 writerIndex - readerIndex 之間。

  24. ByteBuf discardSomeReadBytes(): 類似于discardReadBytes()氓英,不同之處是此方法可能會(huì)根據(jù)其內(nèi)部實(shí)現(xiàn)丟棄一些侯勉、全部或不丟棄讀字節(jié),從而以潛在的額外內(nèi)存消耗為代價(jià)減少總體內(nèi)存帶寬消耗铝阐。

  25. ByteBuf ensureWritable(int minWritableBytes): 擴(kuò)展緩存區(qū)的容量址貌,以便寫入更多數(shù)據(jù),如果沒有超過擴(kuò)展緩存區(qū)的最大容量 maxCapacity徘键,那么就會(huì)擴(kuò)展成功练对。

  26. int ensureWritable(int minWritableBytes, boolean force): 擴(kuò)展緩存區(qū)的容量。與上一個(gè)方法不同吹害,它會(huì)返回狀態(tài)代碼螟凭,來表示擴(kuò)容情況

    • 0 表示緩沖區(qū)有足夠的可寫字節(jié),且其容量不變它呀。
    • 1 表示緩沖區(qū)沒有足夠的字節(jié)螺男,且其容量不變。
    • 2 表示緩沖區(qū)有足夠的可寫字節(jié)钟些,且其容量已增加烟号。
    • 3 表示緩沖區(qū)沒有足夠的字節(jié)绊谭,但其容量已增加到最大政恍。
  27. ByteBuf markReaderIndex(),ByteBuf resetReaderIndex(), ByteBuf markWriterIndex()ByteBuf resetWriterIndex() 用來標(biāo)記和重置索引。

四. 總結(jié)

通過上面的介紹达传,應(yīng)該相信你應(yīng)該可以很輕松地使用緩存區(qū) ByteBuf, 接下來的文章篙耗,我們將講解緩存區(qū) ByteBuf 的不同類型的實(shí)現(xiàn)原理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宪赶,一起剝皮案震驚了整個(gè)濱河市宗弯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搂妻,老刑警劉巖蒙保,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異欲主,居然都是意外死亡邓厕,警方通過查閱死者的電腦和手機(jī)逝嚎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來详恼,“玉大人补君,你說我怎么就攤上這事∶粱ィ” “怎么了挽铁?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)敞掘。 經(jīng)常有香客問我叽掘,道長(zhǎng),這世上最難降的妖魔是什么玖雁? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任够掠,我火速辦了婚禮,結(jié)果婚禮上茄菊,老公的妹妹穿的比我還像新娘疯潭。我一直安慰自己,他們只是感情好面殖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布竖哩。 她就那樣靜靜地躺著,像睡著了一般脊僚。 火紅的嫁衣襯著肌膚如雪相叁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天辽幌,我揣著相機(jī)與錄音增淹,去河邊找鬼。 笑死乌企,一個(gè)胖子當(dāng)著我的面吹牛虑润,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播加酵,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拳喻,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了猪腕?” 一聲冷哼從身側(cè)響起冗澈,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陋葡,沒想到半個(gè)月后亚亲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年捌归,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颊亮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陨溅,死狀恐怖终惑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情门扇,我是刑警寧澤雹有,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站臼寄,受9級(jí)特大地震影響霸奕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吉拳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一质帅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧留攒,春花似錦煤惩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拭宁,卻和暖如春洛退,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杰标。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工兵怯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腔剂。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓媒区,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親桶蝎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子驻仅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容