Skip to content

Latest commit

 

History

History
71 lines (55 loc) · 4.41 KB

buffer.md

File metadata and controls

71 lines (55 loc) · 4.41 KB

Buffer的实现与思考

1.传统Buffer

以字符数组为底层的Buffer实现,以readerwriter指针标记读、写位置,并基于此实现各种功能:

/// +----------------------+-------------------------------------+
/// |      blank part      |  readable bytes  |  writable bytes  |
/// |(CONTENT is read away)|     (CONTENT)    |                  |
/// +----------------------+------------------+------------------+
///                        |                  |                  |
/// 0        <=       readerIndex   <=   writerIndex    <=     size
  • 使用下标代替裸指针,避免数据搬移引起的指针失效

2.Muduo::net::Buffer

std::vector<char>作为底层:

/// +-------------------+-------------------------------------+
/// | prependable bytes |  readable bytes  |  writable bytes  |
/// |                   |     (CONTENT)    |                  |
/// +-------------------+------------------+------------------+
/// |                   |                  |                  |
/// 0       <=     readerIndex   <=   writerIndex    <=     size
  • 在传统的基础上添加了prepend功能,使得可以在不搬移数据的情况下在字符串开头添加一定长度的数据。

  • 写指针到达末尾时,会检查读指针前是否有大量空白;如果是则不会重新申请空间,而是在原空间进行数据搬移,也叫内部腾挪(这催生了我们利用环形Buffer来避免数据搬移的想法)

3.环形Burger::RingBuffer

我们希望利用环形缓冲区来避免数据搬移: 传统Buffer在写指针到达末尾后,除非进行内部腾挪,否则是不能继续写的;而环形Burger::RingBuffer可以无需内部腾挪,直接在缓冲区头部继续写数据

/// +------------------+------------------+-----------------+------------------+
/// |  readable bytes  |  writable  bytes |   min_prepend   |  readable bytes  |
/// |    (CONTENT)     |                  | (kCheapPrepend) |    (CONTENT)     |
/// |                  |          prependable bytes         |                  |
/// +------------------+------------------------------------+------------------+
/// |                  |                                    |                  |
/// 0      <=     writerIndex                          readerIndex   <=       size

实现的环形Burger::RingBuffer有以下要点:

  • 在计算writableBytes务必留出Prepend所需的最小空间(这里是8bytes);当无法留出足够的Prepend空间时,则状态为不可写

  • 在寻找CRLF("\r\n")时,需要考虑其在末尾被截断的情况

  • 在读取int16~int64时,同样需要考虑其在末尾被截断的情况;这种情况需要我们分别读取两部分后再拼接

  • 无论是正常情况(reader指针在writer指针之前)还是环形情况(reader指针在writer指针之后),我们都需要保证reader指针前有一定的prepend区域(否则,考虑写满时,若reader指针在中间,就无法prepend);

  • 这就要求writer转回时,不是从头开始,而是需要在头部留出空白,否则如果reader指针正好停在整个Buffer的最开始处时,则无法进行prepend,如下图所示:

/// +-----------------------+------------------+-----------------+
/// |   readable bytes      |  writable  bytes |   min_prepend   |
/// |      (CONTENT)        |                  | (kCheapPrepend) |
/// |                       |          prependable bytes         |
/// +-----------------------+------------------------------------+
/// |                       |                                    |
/// 0(readerIndex)  <=  writerIndex            <=              size
  • 要解决图示的问题,则需要在整个Buffer前再设置一个Prepend区域;(但我并不喜欢这样麻烦且不优雅的设计。还有更好的解决方案吗?)

4.Burger::IBuffer

IBuffer类似于一个抽象基类,用户可以继承此基类来实现自己所需的Buffer(当然需要实现要求的接口),就像Burger::RingBufferBurger::Buffer都是其子类。

  • 环形Burger::RingBuffer只在内部腾挪次数多的场景下有明显优势,如果整个操作过程完全不涉及到内部腾挪,那么和传统Buffer并无差别,甚至由于各种截断拼接判断还会略慢一些。这也是我们保留Burger::Buffer的原因。