zoukankan      html  css  js  c++  java
  • 略解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流的转换。

  • 相关阅读:
    Educational Codeforces Round 23E
    Educational Codeforces Round 23D
    Codeforces Round #461 (Div. 2)
    HYSBZ
    HDU
    HYSBZ
    HYSBZ
    SPOJ
    点击搜索条件提交form表单
    HTML颜色获取工具,colorpicker
  • 原文地址:https://www.cnblogs.com/scy251147/p/10366626.html
Copyright © 2011-2022 走看看