buffer_bufferedreader - CSDN
精华内容
参与话题
  • Buffer(缓冲器)

    2019-02-28 08:26:23
    什么是Buffer 缓冲区Buffer是暂时存放输入输出数据的一段内存。 JS语言没有二进制数据类型,而在处理TCP和文件流的时候,必须要处理二进制数据。 NodeJS提供了一个Buffer对象来提供对二进制数据的操作 是一个表示...

    什么是Buffer

    • 缓冲区Buffer是暂时存放输入输出数据的一段内存。
    • JS语言没有二进制数据类型,而在处理TCP和文件流的时候,必须要处理二进制数据。
    • NodeJS提供了一个Buffer对象来提供对二进制数据的操作
    • 是一个表示固定内存分配的全局对象,也就是说要放到缓存区中的字节数需要提前确定
    • Buffer好比由一个8位字节元素组成的数组,可以有效的在JavasScript中表示二进制数据 Buffer 类被引入作为 Node.js API 的一部分,使其可以在 TCP 流或文件系统操作等场景中处理二进制数据流。

    Buffer 类的实例类似于整数数组,但 Buffer 的大小是固定的、且在 V8 堆外分配物理内存。 Buffer 的大小在被创建时确定,且无法调整。

    Buffer 类在 Node.js 中是一个全局变量,因此无需使用 require('buffer').Buffer。

    怎么创建buffer? 第一种方式:Buffer.alloc()

     let buffer = Buffer.alloc(6);
     //创建一个长度为6,并且用0填充的Buffer。
     //这样申请方式,内存永远是干净的。
     // 这种声明也比较耗时,因为声明之后,还要把里面的东西手动清空
     //输出:<Buffer 00 00 00 00 00 00>
     
     let buffer2 = Buffer.alloc(6,1);
     // 创建一个长度为 6、且用 0x1 填充的 Buffer。 
     //输出:<Buffer 01 01 01 01 01 01>
    复制代码

    第二种方式:Buffer.allocUnsafe()

    let buffer = Buffer.allocUnsafe(6);
    // 创建一个长度为 6、且未初始化的 Buffer。
    // 这个方法比调用 Buffer.alloc() 更快
    //输出:<Buffer 07 00 00 00 00 00>
    //但里面的东西是不安全的,可能含有旧数据。
    //因此需要使用 fill() 或 write() 重写
    buffer.fill(0);//对buffer进行重写
    //输出:<Buffer 00 00 00 00 00 00>
    复制代码

    定义buffer的3种方式

    1. 通过长度定义
    // 创建一个长度为 10、且用 0 填充的 Buffer。
    let buf1 = Buffer.alloc(10);
    // 创建一个长度为 10、且用 0x1 填充的 Buffer。
    let buf2 = Buffer.alloc(10, 1);
    // 创建一个长度为 10、且未初始化的 Buffer。
    let buf3 = Buffer.allocUnsafe(10);
    复制代码
    1. 通过字符串定义
    let buf = Buffer.from('hello world');
    //输出:<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
    //默认是转成utf8格式的数据,也可以转成其他格式的数据,不支持gbk
    
    复制代码

    3 通过数组定义

    let buf = Buffer.from([1, 2, 3]);
    // 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。
    //<Buffer 01 02 03>
    let buf2 = Buffer.from([16, 17, 18]);
    //<Buffer 10 11 12>
    //Buffer存的都是16进制,但是form存放的都是10进制,所以要除以16
    复制代码

    以上3种方法,是创建buffer的3种方式


    buf.write(string[, offset[, length]][, encoding])

    • string 要写入 buf 的字符串。
    • offset 开始写入 string 前要跳过的字节数。默认: 0。
    • length 要写入的字节数。默认: buf.length - offset。
    • encoding string 的字符编码。默认: 'utf8'。
    • 返回: 写入的字节数。

    操作内存空间

    比如有个字符串'我爱编程',希望将我和爱编程分开输出,也就是第一次将我放到一个变量里,把剩余3个字放到一个变量里。 具体操作:

    // 先申请一个Buffer
    // 一个汉字3个字节,4个汉字12个字节
    let buffer = Buffer.alloc(12);//里面是0填充的,内存是干净的
    // 再构建两个buf1和buf2,然后把他们写到第一个个buffer里
    let buf1 = '我';
    let buf2 = '爱编程';
    // write的参数分别是:写入的内容 ,偏移量,长度,编码格式
    buffer.write(buf1,0,3,'utf8');//往buffer里面写内容buf1,从当前buffer的开头写,所以是第0个,长度是3,因为一个汉字3个字节
    // 再写一个buf2
    buffer.write(buf2,3,9,'utf8');
    console.log(buffer);
    //<Buffer e6 88 91 e7 88 b1 e7 bc 96 e7 a8 8b>
    // 把buffer和字符串进行转换
    console.log(buffer.toString());
    //我爱编程
    复制代码

    进制

    • 0b 2进制
    • 0x 16进制
    • 0o 8进制
    parseInt():任意进制字符串转换为十进制
    parseInt("11", 2); // 3 2进制转10进制
    parseInt("77", 8); // 63 8进制转10进制
    parseInt("e7", 16); //175 16进制转10进制
    复制代码
    toString():10进制转换为其它进制字符串
    (3).toString(2)) // "11" 十进制转2进制
    (17).toString(16) // "11" 十进制转16进制
    (33).toString(32) // "11" 十提制转32进制
    复制代码

    buffer其他常用的方法

    buf.slice([start[, end]]);

    • start : 新建的 Buffer 开始的位置。 默认: 0
    • end : 新建的 Buffer 结束的位置(不包含)。 默认: buf.length
    • 返回 : <Buffer>
    let buffer = Buffer.alloc(6);
    let newBuffer = buffer.slice(0,3);
    newBuffer[0] = 100;
    console.log(buffer)
    //<Buffer 64 00 00 00 00 00>
    //由此可见,buffer里面存的是内存地址
    复制代码

    buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

    • target <Buffer> | <Uint8Array> 要拷贝进的 Buffer 或 Uint8Array。
    • targetStart target 中开始拷贝进的偏移量。 默认: 0
    • sourceStart buf 中开始拷贝的偏移量。 当 targetStart 为 undefined 时忽略。 默认: 0
    • sourceEnd buf 中结束拷贝的偏移量(不包含)。 当 sourceStartundefined 时忽略。 默认: buf.length
    • 返回: <integer> 被拷贝的字节数。

    拷贝 buf 的一个区域的数据到 target 的一个区域,即便 target 的内存区域与 buf 的重叠。

    let buffer = Buffer.alloc(6);
    let buf1 = Buffer.from('一');
    let buf2 = Buffer.from('万');
    //要打印出万一
    // 要把buf2  buf1的内容拷贝到buffer中
    // write和copy的区别:write拷的是字符串,copy拷的是buffer
    // 参数:目标  target中开始拷贝进的偏移量  buf1中开始拷贝的偏移量  buf1中结束拷贝的偏移量(不包含)
    buf1.copy(buffer,3,0,3);//因为一时第二个文字,所以写在第3位(一个文字3个字节,第一个文字被占用,即012被占用)
    buf2.copy(buffer,0,0,3);
    console.log(buffer.toString())
    //输出:万一
    复制代码

    那么问题来了,如何实现copy方法? 首先,copy是Buffer实例上的方法,所以应该定义在Buffer原型上。

    Buffer.prototype.mycopy = function(target, targetStart, sourceStart, sourceEnd){
        /** 
         * 有4个参数
         * 目标  
         * target中开始拷贝进的偏移量  
         * buf1中开始拷贝的偏移量  
         * buf1中结束拷贝的偏移量(不包含)
         * 
        */
        //Buffer跟数组很像,有个迭代的功能
        for(let i = sourceStart;i<sourceEnd; i++){//迭代每一项
            // 从偏移量开始写 
            target[i+targetStart] = this[i];    //迭代每一项赋给目标buffer,this是buf1实例
        } 
    
    }
    buf1.mycopy(buffer,3,0,3);//因为一时第二个文字,所以写在第3位(一个文字3个字节,第一个文字被占用,即012被占用)
    buf2.mycopy(buffer,0,0,3);
    console.log(buffer.toString())
    复制代码

    Buffer.concat(list[, totalLength])

    • list < Array > 要合并的 Buffer 或 Uint8Array 实例的数组
    • totalLength < integer > 合并时 listBuffer 实例的总长度
    • 返回: < Buffer >

    返回一个合并了 list 中所有 Buffer 实例的新建的 Buffer 。 如果 list 中没有元素、或 totalLength 为 0 ,则返回一个新建的长度为 0 的 Buffer 。 如果没有提供 totalLength ,则从 list 中的 Buffer 实例计算得到。 为了计算 totalLength 会导致需要执行额外的循环,所以提供明确的长度会运行更快。 如果提供了 totalLength,totalLength 必须是一个正整数。如果从 list 中计算得到的 Buffer 长度超过了 totalLength,则合并的结果将会被截断为 totalLength 的长度。

    let buffer1 = Buffer.from('前');
    let buffer2 = Buffer.from('端');
    let buffer = Buffer.concat([buffer1,buffer2]).toString();//返回的是新Buffer,要toString()转译一下
    console.log(buffer);
    //输出:前端
    
    let buf = Buffer.concat([buffer1,buffer2],10).toString();
    console.log(buf);
    //如果把长度写多了,会有问题,见下图
    //多写的内容就是0
    复制代码

    split

    buf.indexOf(value[, byteOffset][, encoding])

    • value < string > | < Buffer > | < Uint8Array > | < integer > 要搜索的值
    • byteOffset < integer > buf 中开始搜索的位置。默认: 0
    • encoding < string > 如果 value 是一个字符串,则这是它的字符编码。 默认: 'utf8'
    • 返回: < integer > buf 中 value 首次出现的索引,如果 buf 没包含 value 则返回 -1

    如果 value 是:

    • 字符串,则 value 根据 encoding 的字符编码进行解析。
    • BufferUint8Array,则 value 会被作为一个整体使用。如果要比较部分 Buffer,可使用 buf.slice()。
    • 数值, 则 value 会解析为一个 0 至 255 之间的无符号八位整数值。
    const buf = Buffer.from('this is a buffer');
    
    // 输出: 0
    console.log(buf.indexOf('this'));
    
    // 输出: 2
    console.log(buf.indexOf('is'));
    
    // 输出: 8
    console.log(buf.indexOf(Buffer.from('a buffer')));
    
    // 输出: 8
    // (97 是 'a' 的十进制 ASCII 值)
    console.log(buf.indexOf(97));
    
    // 输出: -1
    console.log(buf.indexOf(Buffer.from('a buffer example')));
    
    // 输出: 8
    console.log(buf.indexOf(Buffer.from('a buffer example').slice(0, 8)));
    
    
    const utf16Buffer = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2');
    
    // 输出: 4
    console.log(utf16Buffer.indexOf('\u03a3', 0, 'ucs2'));
    
    // 输出: 6
    console.log(utf16Buffer.indexOf('\u03a3', -4, 'ucs2'));
    复制代码
    展开全文
  • Buffer的基本用法

    千次阅读 2017-10-17 18:20:43
    1、对buffe.put(**)后,都要buffer.flip();使limit设为当前位置(buffer最后位),且把position设为0。   2、byte[] b = new byte[buffer.remaining()]; 把buffer.长度作为新new的b的长度

    1、对buffe.put(**)后,都要buffer.flip();使limit设为当前位置(buffer最后位),且把position设为0。

     

    2、byte[] b = new byte[buffer.remaining()];

    把buffer.长度作为新new的b的长度

     

    展开全文
  • NIO浅谈之Buffer基本原理

    千次阅读 2018-08-07 22:30:45
    NIO从整体上分类来看可以看做由这几个部分组成:Buffer,Channel,Selector组成。本篇文章从浅谈一下Buffer。 1:Buffer的类型。可以这么来说,不同的数据类型有不同的buffer,例如ByteBuffer,IntBuffer,...

    NIO从整体上分类来看可以看做由这几个部分组成:Buffer,Channel,Selector组成。本篇文章从浅谈一下Buffer。

    1:Buffer的类型。可以这么来说,不同的数据类型有不同的buffer,例如ByteBuffer,IntBuffer,LongBuffer,ShortBuffer。。分别对应的数据类型为byte,int,long,short。

    2:想获取一个Buffer对象的话首先要进行buffer对象的分配,每一个Buffer类都有一个allocate的方法。具体用法:

    ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);

    观察如上的代码,创建一个具体的Buffer对象都要使用相关的Buffer类的allocate方法。到这里,可能就会有同学有疑问了,为什么不能直接new出来。下面,进入ByteBuffer源码里看一眼。

    public abstract class ByteBuffer
        extends Buffer
        implements Comparable<ByteBuffer>
    public static ByteBuffer allocate(int capacity) {
            if (capacity < 0)
                throw new IllegalArgumentException();
            return new HeapByteBuffer(capacity, capacity);
        }

    估计可以看出一些门道来了,ByteBuffer是一个抽象类,allocate是抽象类中的静态方法,通过allocate方法构造的是HeapByteBuffer对象。

    由此,推断出其他数据类型的Buffer也是一个抽象对象,通过allocate方法构造出具体的对象来。因此,又去IntBuffer源码中看了一眼,果不其然,如下图:

    public abstract class IntBuffer
        extends Buffer
        implements Comparable<IntBuffer>
    
    
        public static IntBuffer allocate(int capacity) {
            if (capacity < 0)
                throw new IllegalArgumentException();
            return new HeapIntBuffer(capacity, capacity);
        }

     

    接下来,为了理解buffer的工作原理,看下buffer的三个属性

    capacity【容量】

    position【位置】

    limit【界限】

    关于这三个属性之间的关系,找了一个比较易懂的图片来解释下

    左边的代表写入的时候,右边的代表读取的时候

    当写入的时候,capacity的大小代表利用allocate初始化时候的大小,代表这个缓存块的容量,最多只能向这个缓存块中放入capacity个char,long,int,byte等等。当缓存块满了的时候需要将其清空,才能继续往里面写数据

    position:代笔当前位置,初始化值为0,当一个数据写入buffer中的时候,position会移动到下一个可插入的buffer单元,因此,position的最大值为position-1,

    limit:在写的模式下面,limit表示你最多可以向buffer里面写入多少个buffer数据,在写模式下面,limit=capacity。

    当切换到读模式下面,代表最多能读取到多少数据。因此当切花到读模式下面,limit会被置为写模式下面的position值。因此,你能读取到在写模式下面写入的所有值。

     

    以上的话,就是这三个属性的具体介绍。

    理解了这三个属性之后,对于理解buffer的一些方法有很大的用处。

    1:rewind方法:主要是讲position置为0,因此,我们可以从头开始重新读取一遍数据,limit保持不变。

    2:clear与compact方法。当读取完数据之后可以通过这两个方法来继续让buffer准备好继续写入

    public final Buffer clear() {
            position = 0;
            limit = capacity;
            mark = -1;
            return this;
        }

    因此,通过clear的方法之后,会将position置为0,limit置为容器大小,相当远将容器清空,所以这时候即使有未处理的数据也不会再被读取出来。

    但是compact方法,会将position置为 limit-position,意味着什么,假设limit=10,position=3,意味着这个缓存块我们已经读到第三个,还剩下7个没有读,最新的position=7,也就是最新一次存数据会从第7个开始写,并且会将原来未读取的数据copy一份放在前7个块中。

    public ByteBuffer compact() {
    
            System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
            position(remaining());
            limit(capacity());
            discardMark();
            return this;
    
    
    
        }

    flip 当我们从写模式切换到读模式的时候使用。

     public final Buffer flip() {
            limit = position;
            position = 0;
            mark = -1;
            return this;
        }

    理解了这三个属性的意思之后,看下代码直接明白什么意思了,将limit=position。意思为在读模式下可以读取最多的数据是多少,position=0说明读取开始的位置。

    展开全文
  • Buffer类的详解(转)

    千次阅读 2016-06-16 15:53:22
    Buffer类的详解(转) 这篇蚊帐转自:http://zachary-guo.iteye.com/blog/1457542,作者写的非常好,是我看到的写nio最好的几篇蚊帐,但原文中有一些错误,还有我自己对这方面的一些理解,在这里一并更改了。...

    Buffer类的详解(转)

    这篇蚊帐转自:http://zachary-guo.iteye.com/blog/1457542,作者写的非常好,是我看到的写nio最好的几篇蚊帐,但原文中有一些错误,还有我自己对这方面的一些理解,在这里一并更改了。

    Buffer 类是 java.nio 的构造基础。一个 Buffer 对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里,数据可被存储并在之后用于检索。缓冲区可以被写满或释放。对于每个非布尔原始数据类型都有一个缓冲区类,即 Buffer 的子类有:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer,是没有 BooleanBuffer 之说的。尽管缓冲区作用于它们存储的原始数据类型,但缓冲区十分倾向于处理字节。非字节缓冲区可以在后台执行从字节或到字节的转换,这取决于缓冲区是如何创建的。 
    ◇ 缓冲区的四个属性 
            所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息,这四个属性尽管简单,但其至关重要,需熟记于心:

    • 容量(Capacity):缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。
    • 上界(Limit):缓冲区的第一个不能被读或写的元素。缓冲创建时,limit 的值等于 capacity 的值。假设 capacity = 1024,我们在程序中设置了 limit = 512,说明,Buffer 的容量为 1024,但是从 512 之后既不能读也不能写,因此可以理解成,Buffer 的实际可用大小为 512。
    • 位置(Position):下一个要被读或写的元素的索引。位置会自动由相应的 get() 和 put() 函数更新。 这里需要注意的是positon的位置是从0开始的。
    • 标记(Mark):一个备忘位置。标记在设定前是未定义的(undefined)。使用场景是,假设缓冲区中有 10 个元素,position 目前的位置为 2(也就是如果get的话是第三个元素),现在只想发送 6 - 10 之间的缓冲数据,此时我们可以 buffer.mark(buffer.position()),即把当前的 position 记入 mark 中,然后 buffer.postion(6),此时发送给 channel 的数据就是 6 - 10 的数据。发送完后,我们可以调用 buffer.reset() 使得 position = mark,因此这里的 mark 只是用于临时记录一下位置用的。
    请切记,在使用 Buffer 时,我们实际操作的就是这四个属性的值。我们发现,Buffer 类并没有包括 get() 或 put() 函数。但是,每一个Buffer 的子类都有这两个函数,但它们所采用的参数类型,以及它们返回的数据类型,对每个子类来说都是唯一的,所以它们不能在顶层 Buffer 类中被抽象地声明。它们的定义必须被特定类型的子类所遵从。若不加特殊说明,我们在下面讨论的一些内容,都是以 ByteBuffer 为例,当然,它当然有 get() 和 put() 方法了。 
    ◇ 相对存取和绝对存取

    Java代码

    1. public abstract class ByteBuffer extends Buffer implements Comparable { 
    2. // This is a partial API listing
    3. public abstract byte get( );  
    4. public abstract byte get (int index);  
    5. public abstract ByteBuffer put (byte b);  
    6. public abstract ByteBuffer put (int index, byte b); 

            来看看上面的代码,有不带索引参数的方法和带索引参数的方法。不带索引的 get 和 put,这些调用执行完后,position 的值会自动前进。当然,对于 put,如果调用多次导致位置超出上界(注意,是 limit 而不是 capacity),则会抛出 BufferOverflowException 异常;对于 get,如果位置不小于上界(同样是 limit 而不是 capacity),则会抛出 BufferUnderflowException 异常。这种不带索引参数的方法,称为相对存取,相对存取会自动影响缓冲区的位置属性。带索引参数的方法,称为绝对存取,绝对存储不会影响缓冲区的位置属性,但如果你提供的索引值超出范围(负数或不小于上界),也将抛出 IndexOutOfBoundsException 异常。 
    ◇ 翻转 
            我们把 hello 这个串通过 put 存入一 ByteBuffer 中,如下所示:将 hello 存入 ByteBuffer 中

    Java代码

    1. ByteBuffer buffer = ByteBuffer.allocate(1024); 
    2. buffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o'); 

            此时,position = 5,limit = capacity = 1024。现在我们要从正确的位置从 buffer 读数据,我们可以把 position 置为 0,那么字符串的结束位置在哪呢?这里上界该出场了。如果把上界设置成当前 position 的位置,即 5,那么 limit 就是结束的位置。上界属性指明了缓冲区有效内容的末端。人工实现翻转:

    Java代码

    1. buffer.limit(buffer.position()).position(0); 

            但这种从填充到释放状态的缓冲区翻转是API设计者预先设计好的,他们为我们提供了一个非常便利的函数:buffer.flip()。另外,rewind() 函数与 flip() 相似,但不影响上界属性,它只是将位置值设回 0。在进行buffer读操作的时候,一般都会使用buffer.flip()函数。  
    ◇ 释放(Drain) 
            这里的释放,指的是缓冲区通过 put 填充数据后,然后被读出的过程。上面讲了,要读数据,首先得翻转。那么怎么读呢?hasRemaining() 会在释放缓冲区时告诉你是否已经达到缓冲区的上界:hasRemaining()函数和Remaining()函数有密切的功能,

    Java代码

    1. for (int i = 0; buffer.hasRemaining(); i++) { 
    2.     myByteArray[i] = buffer.get(); 

            很明显,上面的代码,每次都要判断元素是否到达上界。我们可以做:改变后的释放过程

    Java代码

    1. int count = buffer.hasRemaining(); 
    2. for (int i = 0; i < count; i++) { 
    3.     myByteArray[i] = buffer.get(); 

            第二段代码看起来很高效,但请注意,缓冲区并不是多线程安全的。如果你想以多线程同时存取特定的缓冲区,你需要在存取缓冲区之前进行同步。因此,使用第二段代码的前提是,你对缓冲区有专门的控制。 
    ◇ buffer.clear() 
            clear() 函数将缓冲区重置为空状态。它并不改变缓冲区中的任何数据元素,而是仅仅将 limit 设为容量的值,并把 position 设回 0。 
    ◇ Compact(不知咋翻译,压缩?紧凑?) 
            有时候,我们只想释放出一部分数据,即只读取部分数据。当然,你可以把 postion 指向你要读取的第一个数据的位置,将 limit 设置成最后一个元素的位置 + 1。但是,一旦缓冲区对象完成填充并释放,它就可以被重新使用了。所以,缓冲区一旦被读取出来,已经没有使用价值了。
            以 Mellow 为例,填充后为 Mellow,但如果我们仅仅想读取 llow。读取完后,缓冲区就可以重新使用了。Me 这两个位置对于我们而言是没用的。我们可以将 llow 复制至 0 - 3 上,Me 则被冲掉。但是 4 和 5 仍然为 o 和 w。这个事我们当然可以自行通过 get 和 put 来完成,但 api 给我们提供了一个 compact() 的函数,此函数比我们自己使用 get 和 put 要高效的多。 
     
    Compact 之前的缓冲区 
            buffer.compact() 会使缓冲区的状态图如下图所示: 
     
    Compact 之后的缓冲区 
            这里发生了几件事:

    1. 数据元素 2 - 5 被复制到 0 - 3 位置,位置 4 和 5 不受影响,但现在正在或已经超出了当前位置,因此是“死的”。它们可以被之后的 put() 调用重写。
    2. Position 已经被设为被复制的数据元素的数目,也就是说,缓冲区现在被定位在缓冲区中最后一个“存活”元素的后一个位置。
    3. 上界属性被设置为容量的值,因此缓冲区可以被再次填满。
    4. 调用 compact() 的作用是丢弃已经释放的数据,保留未释放的数据,并使缓冲区对重新填充容量准备就绪。该例子中,你当然可以将 Me 之前已经读过,即已经被释放过。

    ◇ 缓冲区的比较 
            有时候比较两个缓冲区所包含的数据是很有必要的。所有的缓冲区都提供了一个常规的 equals() 函数用以测试两个缓冲区的是否相等,以及一个 compareTo() 函数用以比较缓冲区。 
            两个缓冲区被认为相等的充要条件是:
    • 两个对象类型相同。包含不同数据类型的 buffer 永远不会相等,而且buffer 绝不会等于非 buffer对象。
    • 两个对象都剩余同样数量(limit - position)的元素。Buffer 的容量不需要相同,而且缓冲区中剩余数据的索引也不必相同。
    • 在每个缓冲区中应被 get() 函数返回的剩余数据元素序列([position, limit - 1] 位置对应的元素序列)必须一致。
     
    两个被认为是相等的缓冲区 
     
    两个被认为是不相等的缓冲区 
            缓冲区也支持用 compareTo() 函数以词典顺序进行比较,当然,这是所有的缓冲区实现了 java.lang.Comparable 语义化的接口。这也意味着缓冲区数组可以通过调用 java.util.Arrays.sort() 函数按照它们的内容进行排序。 
            与 equals() 相似,compareTo() 不允许不同对象间进行比较。但 compareTo()更为严格:如果你传递一个类型错误的对象,它会抛出 ClassCastException 异常,但 equals() 只会返回 false。 
            比较是针对每个缓冲区你剩余数据(从 position 到 limit)进行的,与它们在 equals() 中的方式相同,直到不相等的元素被发现或者到达缓冲区的上界。如果一个缓冲区在不相等元素发现前已经被耗尽,较短的缓冲区被认为是小于较长的缓冲区。这里有个顺序问题:下面小于零的结果(表达式的值为 true)的含义是 buffer2 < buffer1。切记,这代表的并不是 buffer1 < buffer2。

    Java代码 收藏代码

    1. if (buffer1.compareTo(buffer2) < 0) { 
    2. // do sth, it means buffer2 < buffer1,not buffer1 < buffer2
    3.     doSth(); 

    ◇ 批量移动 
            缓冲区的设计目的就是为了能够高效传输数据,一次移动一个数据元素并不高效。如你在下面的程序清单中所看到的那样,buffer API 提供了向缓冲区你外批量移动数据元素的函数:

    Java代码 收藏代码

    1. public abstract class ByteBuffer extends Buffer implements Comparable { 
    2. public ByteBuffer get(byte[] dst); 
    3. public ByteBuffer get(byte[] dst, int offset, int length); 
    4. public final ByteBuffer put(byte[] src);  
    5. public ByteBuffer put(byte[] src, int offset, int length); 

            如你在上面的程序清单中所看到的那样,buffer API 提供了向缓冲区内外批量移动数据元素的函数。以 get 为例,它将缓冲区中的内容复制到指定的数组中,当然是从 position 开始咯。第二种形式使用 offset 和 length 参数来指定复制到目标数组的子区间。这些批量移动的合成效果与前文所讨论的循环是相同的,但是这些方法可能高效得多,因为这种缓冲区实现能够利用本地代码或其他的优化来移动数据。 
            批量移动总是具有指定的长度。也就是说,你总是要求移动固定数量的数据元素。因此,get(dist) 和 get(dist, 0, dist.length) 是等价的。 
            对于以下几种情况的数据复制会发生异常:

    • 如果你所要求的数量的数据不能被传送,那么不会有数据被传递,缓冲区的状态保持不变,同时抛出BufferUnderflowException异常。
    • 如果缓冲区中的数据不够完全填满数组,你会得到一个异常。这意味着如果你想将一个小型缓冲区传入一个大型数组,你需要明确地指定缓冲区中剩余的数据长度。
               如果缓冲区存有比数组能容纳的数量更多的数据,你可以重复利用如下代码进行读取:

    Java代码 收藏代码

    1. byte[] smallArray = new Byte[10]; 
    2. while (buffer.hasRemaining()) { 
    3. int length = Math.min(buffer.remaining(), smallArray.length); 
    4.     buffer.get(smallArray, 0, length); 
    5. // 每取出一部分数据后,即调用 processData 方法,length 表示实际上取到了多少字节的数据
    6.     processData(smallArray, length); 

            put() 的批量版本工作方式相似,只不过它是将数组里的元素写入 buffer 中而已,这里不再赘述。 
    ◇ 创建缓冲区 
            Buffer 的七种子类,没有一种能够直接实例化,它们都是抽象类,但是都包含静态工厂方法来创建相应类的新实例。这部分讨论中,将以  CharBuffer 类为例,对于其它六种主要的缓冲区类也是适用的。下面是创建一个缓冲区的关键函数,对所有的缓冲区类通用(要按照需要替换类名):

    Java代码 收藏代码

    1. public abstract class CharBuffer extends Buffer implements CharSequence, Comparable { 
    2. // This is a partial API listing
    3. public static CharBuffer allocate (int capacity); 
    4. public static CharBuffer wrap (char [] array); 
    5. public static CharBuffer wrap (char [] array, int offset, int length); 
    6. public final boolean hasArray(); 
    7. public final char [] array(); 
    8. public final int arrayOffset(); 

            新的缓冲区是由分配(allocate)或包装(wrap)操作创建的。分配(allocate)操作创建一个缓冲区对象并分配一个私有的空间来储存容量大小的数据元素。包装(wrap)操作创建一个缓冲区对象但是不分配任何空间来储存数据元素。它使用你所提供的数组作为存储空间来储存缓冲区中的数据元素。demos:

    Java代码 收藏代码

    1. // 这段代码隐含地从堆空间中分配了一个 char 型数组作为备份存储器来储存 100 个 char 变量。
    2. CharBuffer charBuffer = CharBuffer.allocate (100); 
    3. /**
    4. * 这段代码构造了一个新的缓冲区对象,但数据元素会存在于数组中。这意味着通过调用 put() 函数造成的对缓
    5. * 冲区的改动会直接影响这个数组,而且对这个数组的任何改动也会对这个缓冲区对象可见。
    6. */
    7. char [] myArray = new char [100];  
    8. CharBuffer charbuffer = CharBuffer.wrap (myArray); 
    9. /**
    10. * 带有 offset 和 length 作为参数的 wrap() 函数版本则会构造一个按照你提供的 offset 和 length 参
    11. * 数值初始化 position 和 limit 的缓冲区。
    12. *
    13. * 这个函数并不像你可能认为的那样,创建了一个只占用了一个数组子集的缓冲区。这个缓冲区可以存取这个数组
    14. * 的全部范围;offset 和 length 参数只是设置了初始的状态。调用 clear() 函数,然后对其进行填充,
    15. * 直到超过 limit,这将会重写数组中的所有元素。
    16. *
    17. * slice() 函数可以提供一个只占用备份数组一部分的缓冲区。
    18. *
    19. * 下面的代码创建了一个 position 值为 12,limit 值为 54,容量为 myArray.length 的缓冲区。
    20. */
    21. CharBuffer charbuffer = CharBuffer.wrap (myArray, 12, 42); 

            通过 allocate() 或者 wrap() 函数创建的缓冲区通常都是间接的。间接的缓冲区使用备份数组,你可以通过上面列出的 api 函数获得对这些数组的存取权。 
            boolean 型函数 hasArray() 告诉你这个缓冲区是否有一个可存取的备份数组。如果这个函数的返回 true,array() 函数会返回这个缓冲区对象所使用的数组存储空间的引用。如果 hasArray() 函数返回 false,不要调用 array() 函数或者 arrayOffset() 函数。如果你这样做了你会得到一个 UnsupportedOperationException 异常。 
            如果一个缓冲区是只读的,它的备份数组将会是超出 limit 的,即使一个数组对象被提供给 wrap() 函数。调用 array() 函数或 arrayOffset() 会抛出一个 ReadOnlyBufferException 异常以阻止你得到存取权来修改只读缓冲区的内容。如果你通过其它的方式获得了对备份数组的存取权限,对这个数组的修改也会直接影响到这个只读缓冲区。 
            arrayOffset(),返回缓冲区数据在数组中存储的开始位置的偏移量(从数组头 0 开始计算)。如果你使用了带有三个参数的版本的 wrap() 函数来创建一个缓冲区,对于这个缓冲区,arrayOffset() 会一直返回 0。不理解吗?offset 和 length 只是指示了当前的 position 和 limit,是一个瞬间值,可以通过 clear() 来从 0 重新存数据,所以 arrayOffset() 返回的是 0。当然,如果你切分(slice() 函数)了由一个数组提供存储的缓冲区,得到的缓冲区可能会有一个非 0 的数组偏移量。 
    ◇ 复制缓冲区 
            缓冲区不限于管理数组中的外部数据,它们也能管理其他缓冲区中的外部数据。当一个管理其他缓冲器所包含的数据元素的缓冲器被创建时,这个缓冲器被称为视图缓冲器。 
            视图存储器总是通过调用已存在的存储器实例中的函数来创建。使用已存在的存储器实例中的工厂方法意味着视图对象为原始存储器的你部实现细节私有。数据元素可以直接存取,无论它们是存储在数组中还是以一些其他的方式,而不需经过原始缓冲区对象的 get()/put() API。如果原始缓冲区是直接缓冲区,该缓冲区(视图缓冲区)的视图会具有同样的效率优势。 
            继续以 CharBuffer 为例,但同样的操作可被用于任何基本的缓冲区类型。用于复制缓冲区的 api:

    Java代码 收藏代码

    1. public abstract class CharBuffer extends Buffer implements CharSequence, Comparable { 
    2. // This is a partial API listing
    3. public abstract CharBuffer duplicate(); 
    4. public abstract CharBuffer asReadOnlyBuffer(); 
    5. public abstract CharBuffer slice(); 

    ● duplidate() 
            复制一个缓冲区会创建一个新的 Buffer 对象,但并不复制数据。原始缓冲区和副本都会操作同样的数据元素。 
            duplicate() 函数创建了一个与原始缓冲区相似的新缓冲区。两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的 position、limit 和 mark 属性。对一个缓冲区你的数据元素所做的改变会反映在另外一个缓冲区上。这一副本缓冲区具有与原始缓冲区同样的数据视图。如果原始的缓冲区为只读,或者为直接缓冲区,新的缓冲区将继承这些属性。duplicate() 复制缓冲区:

    Java代码 收藏代码

    1. CharBuffer buffer = CharBuffer.allocate(8); 
    2. buffer.position(3).limit(6).mark().position (5); 
    3. CharBuffer dupeBuffer = buffer.duplicate(); 
    4. buffer.clear(); 

     
    复制一个缓冲区 
    ● asReadOnlyBuffer() 
            asReadOnlyBuffer() 函数来生成一个只读的缓冲区视图。这与duplicate() 相同,除了这个新的缓冲区不允许使用 put(),并且其 isReadOnly() 函数将会返回 true。 
            如果一个只读的缓冲区与一个可写的缓冲区共享数据,或者有包装好的备份数组,那么对这个可写的缓冲区或直接对这个数组的改变将反映在所有关联的缓冲区上,包括只读缓冲区。 
    ● slice() 
            分割缓冲区与复制相似,但 slice() 创建一个从原始缓冲区的当前 position 开始的新缓冲区,并且其容量是原始缓冲区的剩余元素数量(limit - position)。这个新缓冲区与原始缓冲区共享一段数据元素子序列。分割出来的缓冲区也会继承只读和直接属性。slice() 分割缓冲区:

    Java代码 收藏代码

    1. CharBuffer buffer = CharBuffer.allocate(8); 
    2. buffer.position(3).limit(5); 
    3. CharBuffer sliceBuffer = buffer.slice(); 

     
    创建分割缓冲区 
    ◇ 字节缓冲区(ByteBuffer) 
            ByteBuffer 只是 Buffer 的一个子类,但字节缓冲区有字节的独特之处。字节缓冲区跟其他缓冲区类型最明显的不同在于,它可以成为通道所执行的 I/O 的源头或目标,后面你会发现通道只接收 ByteBuffer 作为参数。 
            字节是操作系统及其 I/O 设备使用的基本数据类型。当在 JVM 和操作系统间传递数据时,将其他的数据类型拆分成构成它们的字节是十分必要的,系统层次的 I/O 面向字节的性质可以在整个缓冲区的设计以及它们互相配合的服务中感受到。同时,操作系统是在内存区域中进行 I/O 操作。这些内存区域,就操作系统方面而言,是相连的字节序列。于是,毫无疑问,只有字节缓冲区有资格参与 I/O 操作。 
            非字节类型的基本类型,除了布尔型都是由组合在一起的几个字节组成的。那么必然要引出另外一个问题:字节顺序。 
            多字节数值被存储在内存中的方式一般被称为 endian-ness(字节顺序)。如果数字数值的最高字节 - big end(大端),位于低位地址(即 big end 先写入内存,先写入的内存的地址是低位的,后写入内存的地址是高位的),那么系统就是大端字节顺序。如果最低字节最先保存在内存中,那么系统就是小端字节顺序。在 java.nio 中,字节顺序由 ByteOrder 类封装:

    Java代码 收藏代码

    1. package java.nio; 
    2. public final class ByteOrder { 
    3. public static final ByteOrder BIG_ENDIAN; 
    4. public static final ByteOrder LITTLE_ENDIAN; 
    5. public static ByteOrder nativeOrder(); 
    6. public String toString(); 

            ByteOrder 类定义了决定从缓冲区中存储或检索多字节数值时使用哪一字节顺序的常量。如果你需要知道 JVM 运行的硬件平台的固有字节顺序,请调用静态类函数 nativeOrder()。 
    每个缓冲区类都具有一个能够通过调用 order() 查询的当前字节顺序:

    Java代码 收藏代码

    1. public abstract class CharBuffer extends Buffer implements Comparable, CharSequence { 
    2. // This is a partial API listing
    3. public final ByteOrder order(); 

            这个函数从 ByteOrder 返回两个常量之一。对于除了 ByteBuffer 之外的其他缓冲区类,字节顺序是一个只读属性,并且可能根据缓冲区的建立方式而采用不同的值。除了 ByteBuffer,其他通过 allocate() 或 wrap() 一个数组所创建的缓冲区将从 order() 返回与 ByteOrder.nativeOrder() 相同的数值。这是因为包含在缓冲区中的元素在 JVM 中将会被作为基本数据直接存取。 
            ByteBuffer 类有所不同:默认字节顺序总是 ByteBuffer.BIG_ENDIAN,无论系统的固有字节顺序是什么。Java 的默认字节顺序是大端字节顺序,这允许类文件等以及串行化的对象可以在任何 JVM 中工作。如果固有硬件字节顺序是小端,这会有性能隐患。在使用固有硬件字节顺序时,将 ByteBuffer 的内容当作其他数据类型存取很可能高效得多。 
            为什么 ByteBuffer 类需要一个字节顺序?字节不就是字节吗?ByteBuffer 对象像其他基本数据类型一样,具有大量便利的函数用于获取和存放缓冲区内容。这些函数对字节进行编码或解码的方式取决于 ByteBuffer 当前字节顺序的设定。ByteBuffer 的字节顺序可以随时通过调用以 ByteOrder.BIG_ENDIAN 或 ByteOrder.LITTL_ENDIAN 为参数的 order() 函数来改变:

    Java代码 收藏代码

    1. public abstract class ByteBuffer extends Buffer implements Comparable { 
    2. // This is a partial API listing
    3. public final ByteOrder order(); 
    4. public final ByteBuffer order(ByteOrder bo); 

            如果一个缓冲区被创建为一个 ByteBuffer 对象的视图,,那么 order() 返回的数值就是视图被创建时其创建源头的 ByteBuffer 的字节顺序。视图的字节顺序设定在创建后不能被改变,而且如果原始的字节缓冲区的字节顺序在之后被改变,它也不会受到影响。 
    ◇ 直接缓冲区 
            内核空间(与之相对的是用户空间,如 JVM)是操作系统所在区域,它能与设备控制器(硬件)通讯,控制着用户区域进程(如 JVM)的运行状态。最重要的是,所有的 I/O 都直接(物理内存)或间接(虚拟内存)通过内核空间。 
            当进程(如 JVM)请求 I/O 操作的时候,它执行一个系统调用将控制权移交给内核。当内核以这种方式被调用,它随即采取任何必要步骤,找到进程所需数据,并把数据传送到用户空间你的指定缓冲区。内核试图对数据进行高速缓存或预读取,因此进程所需数据可能已经在内核空间里了。如果是这样,该数据只需简单地拷贝出来即可。如果数据不在内核空间,则进程被挂起,内核着手把数据读进内存。 
     
    I/O 缓冲区操作简图 
            从图中你可能会觉得,把数据从内核空间拷贝到用户空间似乎有些多余。为什么不直接让磁盘控制器把数据送到用户空间的缓冲区呢?首先,硬件通常不能直接访问用户空间。其次,像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而用户进程请求的可能是任意大小的或非对齐的数据块。在数据往来于用户空间与存储设备的过程中,内核负责数据的分解、再组合工作,因此充当着中间人的角色。 
            因此,操作系统是在内存区域中进行 I/O 操作。这些内存区域,就操作系统方面而言,是相连的字节序列,这也意味着I/O操作的目标内存区域必须是连续的字节序列。在 JVM中,字节数组可能不会在内存中连续存储(因为 JAVA 有 GC 机制),或者无用存储单元(会被垃圾回收)收集可能随时对其进行移动。 
            出于这个原因,引入了直接缓冲区的概念。直接字节缓冲区通常是 I/O 操作最好的选择。非直接字节缓冲区(即通过 allocate() 或 wrap() 创建的缓冲区)可以被传递给通道,但是这样可能导致性能损耗。通常非直接缓冲不可能成为一个本地 I/O 操作的目标。 
            如果你向一个通道中传递一个非直接 ByteBuffer 对象用于写入,通道可能会在每次调用中隐含地进行下面的操作:

    1. 创建一个临时的直接 ByteBuffer 对象。
    2. 将非直接缓冲区的内容复制到临时直接缓冲区中。
    3. 使用临时直接缓冲区执行低层 I/O 操作。
    4. 临时直接缓冲区对象离开作用域,并最终成为被回收的无用数据。
               这可能导致缓冲区在每个 I/O 上复制并产生大量对象,而这种事都是我们极力避免的。如果你仅仅为一次使用而创建了一个缓冲区,区别并不是很明显。另一方面,如果你将在一段高性能脚本中重复使用缓冲区,分配直接缓冲区并重新使用它们会使你游刃有余。 
            直接缓冲区可能比创建非直接缓冲区要花费更高的成本,它使用的内存是通过调用本地操作系统方面的代码分配的,绕过了标准 JVM 堆栈,不受垃圾回收支配,因为它们位于标准 JVM 堆栈之外。 
            直接 ByteBuffer 是通过调用具有所需容量的 ByteBuffer.allocateDirect() 函数产生的。注意,wrap() 函数所创建的被包装的缓冲区总是非直接的。与直接缓冲区相关的 api:

    Java代码 收藏代码

    1. public abstract class ByteBuffer extends Buffer implements Comparable { 
    2. // This is a partial API listing
    3. public static ByteBuffer allocateDirect (int capacity); 
    4. public abstract boolean isDirect(); 

            所有的缓冲区都提供了一个叫做 isDirect() 的 boolean 函数,来测试特定缓冲区是否为直接缓冲区。但是,ByteBuffer 是唯一可以被分配成直接缓冲区的 Buffer。尽管如此,如果基础缓冲区是一个直接 ByteBuffer,对于非字节视图缓冲区,isDirect() 可以是 true。 
    ◇ 视图缓冲区 
            I/O 基本上可以归结成组字节数据的四处传递,在进行大数据量的 I/O 操作时,很又可能你会使用各种 ByteBuffer 类去读取文件内容,接收来自网络连接的数据,等等。ByteBuffer 类提供了丰富的 API 来创建视图缓冲区。 
            视图缓冲区通过已存在的缓冲区对象实例的工厂方法来创建。这种视图对象维护它自己的属性,容量,位置,上界和标记,但是和原来的缓冲区共享数据元素。 
            每一个工厂方法都在原有的 ByteBuffer 对象上创建一个视图缓冲区。调用其中的任何一个方法都会创建对应的缓冲区类型,这个缓冲区是基础缓冲区的一个切分,由基础缓冲区的位置和上界决定。新的缓冲区的容量是字节缓冲区中存在的元素数量除以视图类型中组成一个数据类型的字节数,在切分中任一个超过上界的元素对于这个视图缓冲区都是不可见的。视图缓冲区的第一个元素从创建它的 ByteBuffer 对象的位置开始(positon() 函数的返回值)。来自 ByteBuffer 创建视图缓冲区的工厂方法:

    Java代码 收藏代码

    1. public abstract class ByteBuffer extends Buffer implements Comparable { 
    2. // This is a partial API listing
    3. public abstract CharBuffer asCharBuffer(); 
    4. public abstract CharBuffer asShortBuffer( ); 
    5. public abstract CharBuffer asIntBuffer( ); 
    6. public abstract CharBuffer asLongBuffer( ); 
    7. public abstract CharBuffer asFloatBuffer( ); 
    8. public abstract CharBuffer asDoubleBuffer( ); 

            下面的代码创建了一个 ByteBuffer 缓冲区的 CharBuffer 视图。演示 7 个字节的 ByteBuffer 的 CharBuffer 视图:

    Java代码 收藏代码

    1. /**
    2. * 1 char = 2 byte,因此 7 个字节的 ByteBuffer 最终只会产生 capacity 为 3 的 CharBuffer。
    3. *
    4. * 无论何时一个视图缓冲区存取一个 ByteBuffer 的基础字节,这些字节都会根据这个视图缓冲区的字节顺序设
    5. * 定被包装成一个数据元素。当一个视图缓冲区被创建时,视图创建的同时它也继承了基础 ByteBuffer 对象的
    6. * 字节顺序设定,这个视图的字节排序不能再被修改。字节顺序设定决定了这些字节对是怎么样被组合成字符
    7. * 型变量的,这样可以理解为什么 ByteBuffer 有字节顺序的概念了吧。
    8. */
    9. ByteBuffer byteBuffer = ByteBuffer.allocate (7).order (ByteOrder.BIG_ENDIAN); 
    10. CharBuffer charBuffer = byteBuffer.asCharBuffer(); 

     
    7 个 字节的 ByteBuffer 的 CharBuffer 视图 
    ◇ 数据元素视图 
    ByteBuffer 类为每一种原始数据类型提供了存取的和转化的方法:

    Java代码 收藏代码

    1. public abstract class ByteBuffer extends Buffer implements Comparable { 
    2. public abstract short getShort( );  
    3. public abstract short getShort(int index); 
    4. public abstract short getInt( );  
    5. public abstract short getInt(int index); 
    6.     ...... 
    7. public abstract ByteBuffer putShort(short value);  
    8. public abstract ByteBuffer putShort(int index, short value); 
    9. public abstract ByteBuffer putInt(int value);  
    10. public abstract ByteBuffer putInt(int index, int value); 
    11.     ....... 

            这些函数从当前位置开始存取 ByteBuffer 的字节数据,就好像一个数据元素被存储在那里一样。根据这个缓冲区的当前的有效的字节顺序,这些字节数据会被排列或打乱成需要的原始数据类型。 
            如果 getInt() 函数被调用,从当前的位置开始的四个字节会被包装成一个 int 类型的变量然后作为函数的返回值返回。实际的返回值取决于缓冲区的当前的比特排序(byte-order)设置。不同字节顺序取得的值是不同的:

    Java代码 收藏代码

    1. // 大端顺序
    2. int value = buffer.order(ByteOrder.BIG_ENDIAN).getInt(); 
    3. // 小端顺序
    4. int value = buffer.order(ByteOrder.LITTLE_ENDIAN).getInt(); 
    5. // 上述两种方法取得的 int 是不一样的,因此在调用此类方法前,请确保字节顺序是你所期望的

            如果你试图获取的原始类型需要比缓冲区中存在的字节数更多的字节,会抛出 BufferUnderflowException。

    原文地址:http://zachary-guo.iteye.com/blog/1457542

    展开全文
  • Buffer类(最详细的解析)

    千次阅读 2016-11-12 19:43:30
    Buffer 类是 java.nio 的构造基础。一个 Buffer 对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里,数据可被存储并在之后用于检索。缓冲区可以被写满或释放。对于每个非布尔原始数据类型都...
  • 缓冲区是包在一个对象内的基本数据元素数组,Buffer类相比一个简单的数组的优点是它将关于数据的数据内容和信息包含在一个单一的对象中。Buffer的属性容量(capacity):缓冲区能够容纳的数据元素的最大数量。这一...
  • buffer几种用法

    千次阅读 2010-11-30 10:46:00
    size_tbytes_transferred=sock.receive(boost::asio::buffer(d1));buffer 大小是自动管理的同样std::vector<char>d2(128);bytes_transferred=sock.receive(boost::asio::buffer(d2));还可以基于boost::arrayboost:...
  • 缓冲区(Buffer)

    万次阅读 2018-10-11 21:38:08
    缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对I/O的数据做临时存储,这部分预留的内存空间叫缓冲区。 使用缓冲区有两个好处: 1、减少实际物理读写次数 2、缓冲区在创建时就被分配内存,这块内存区域...
  • 由一个Buffer而引发的思考

    千次阅读 2020-04-13 22:51:26
     ...简单介绍一下背景:Buffer电路的特点,起到“承上启下”的作用,即Buffer在输入端匹配与之相同量 级别的高阻,而在输出端匹配与之相同量级别的低阻用于驱动较大电流(在低负载情况下也能正...
  • 双缓冲(Double Buffer)原理和使用

    万次阅读 多人点赞 2016-05-04 13:23:17
    一、双缓冲作用    双缓冲甚至是多缓冲,在许多情况下都很有用。一般需要使用双缓冲区的地方都是由于“生产者”和“消费者”供需不一致所造成的。这样的情况在很多地方后可能会发生,使用多缓冲可以很好的解决。...
  • Command Buffer是Unity5新增的一个灰常灰常强大的功能。先祭出官方介绍和文档。我们在渲染的时候,给OpenGL或者DX的就是一系列的指令,比如glDrawElement,glClear等等,这些东西目前是引擎去调用的,而Unity也为...
  • 习惯用 Json、XML 数据存储格式的你们,相信大多都没听过Protocol Buffer Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存储格式,性能比 Json、XML 真的强!太!多! 由于 Google出品,我相信...
  • node使用buffer生成图片

    万次阅读 2019-12-27 09:22:19
    buffer是node里的一个模块,这个模块的出现是因为js没有阅读和操作二进制数据流而出现的1、buffer是什么及作用? Buffer顾名思义叫缓冲区,用于存储速度不同步的设备或优先级不同的设备之间传输数据;通过buffer...
  • C语言 ringBuffer 实现

    万次阅读 2018-12-27 18:53:54
    一、 ringBuffer 介绍 ringBuffer 称作环形缓冲,也有叫 circleBuffer 的。就是取内存中一块连续的区域用作环形缓冲区的数据存储区。这块连续的存储会被反复使用,向 ringBuffer 写入数据总是从写指针的位置开始...
  • FPGA之乒乓Buffer

    千次阅读 2019-04-02 19:32:18
    1.关于乒乓Buffer: 对于数据传输速率大于数据处理速率的情况: 乒乓Buffer的应用场景:当后面的处理单元在工作期间,前面的buffer的内容不能被释放。或者,在处理单元工作期间,buffer的特定地址的内容不止被...
  • matlab buffer的使用

    千次阅读 2018-03-12 19:56:07
    buffer matlab中的buffer函数可以将信号缓存为数据帧矩阵。 y = buffer(x,n) y = buffer(x,n,p) [y,z] = buffer(...) [y,z,opt] = buffer(...) 分割长度L的信号x成不重叠的长度为n的数据帧,每一帧占据...
  • Cache 和 Buffer的区别

    千次阅读 2019-11-28 13:26:38
    Cache vs Buffer 高速缓存和缓冲区缓存区cache和缓冲区buffer都是临时存储区,但它们在许多方面有所不同。缓冲区buffer主要存在于RAM中,作为CPU暂时存储数据的区域,例如,当计算机和其他设备具有不同的速度时, ...
  •  Constant buffer(cb)是DX10中引入的概念,它取代了DX9时代GPU常量寄存器的概念,允许通过一块大小可变的buffer向shader提供常量数据,而不是之前数量非常受限的n个寄存器,这也是我们遇到的第一种可在shader着色...
  • 为什么插入buffer能够增加驱动能力?

    万次阅读 多人点赞 2018-07-08 19:25:13
    buffer是什么?所谓增加bufferbuffer一般是几级器件尺寸逐步增大的反相器或类似结构的电路,以使得电阻在获得所需的驱动能力时,在功耗延时积上也达到最优。前后级的最佳驱动比例在2.718左右。buffer实际就是两...
  • 16进制与buffer相互转换

    千次阅读 2020-01-07 21:23:56
    16进制与buffer相互转换 一个字节是8位,相当于16进制2位,因此为了方便表示,很多情况下,buffer打印出来是16进制显示的,但是本质上还是2进制,就好比buffer里边的 11111111 打印出来之后就会显示 ff 首先说说16...
1 2 3 4 5 ... 20
收藏数 851,798
精华内容 340,719
关键字:

buffer