muduo源码剖析之Buffer缓冲区类

news2024/11/25 19:51:11

简介

Buffer封装了一个可变长的buffer,支持廉价的前插操作,以及内部挪腾操作避免额外申请空间

使用vector作为缓冲区(可自动调整扩容)

设计图

image-20230601164911588

源码剖析

已经编写好注释

buffer.h

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.

#ifndef MUDUO_NET_BUFFER_H
#define MUDUO_NET_BUFFER_H

#include "muduo/base/copyable.h"
#include "muduo/base/StringPiece.h"
#include "muduo/base/Types.h"

#include "muduo/net/Endian.h"

#include <algorithm>
#include <vector>

#include <assert.h>
#include <string.h>
//#include <unistd.h>  // ssize_t

namespace muduo
{
namespace net
{

/// A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer
///
/// @code
/// +-------------------+------------------+------------------+
/// | prependable bytes |  readable bytes  |  writable bytes  |
/// |                   |     (CONTENT)    |                  |
/// +-------------------+------------------+------------------+
/// |                   |                  |                  |
/// 0      <=      readerIndex   <=   writerIndex    <=     size
/// @endcode
class Buffer : public muduo::copyable
{
 public:
  static const size_t kCheapPrepend = 8;//预留8字节
  static const size_t kInitialSize = 1024;//缓冲区初始化大小

  explicit Buffer(size_t initialSize = kInitialSize)
    : buffer_(kCheapPrepend + initialSize),
      readerIndex_(kCheapPrepend),
      writerIndex_(kCheapPrepend)
  {
    assert(readableBytes() == 0);
    assert(writableBytes() == initialSize);
    assert(prependableBytes() == kCheapPrepend);
  }

  // implicit copy-ctor, move-ctor, dtor and assignment are fine
  // NOTE: implicit move-ctor is added in g++ 4.6

  void swap(Buffer& rhs)//交换缓冲区
  {
    buffer_.swap(rhs.buffer_);
    std::swap(readerIndex_, rhs.readerIndex_);
    std::swap(writerIndex_, rhs.writerIndex_);
  }

  size_t readableBytes() const//剩余可读字节大小
  { return writerIndex_ - readerIndex_; }

  size_t writableBytes() const//剩余可写字节大小
  { return buffer_.size() - writerIndex_; }

  size_t prependableBytes() const//已读字节大小
  { return readerIndex_; }

  const char* peek() const//readIndex
  { return begin() + readerIndex_; }

  const char* findCRLF() const
  {
    // FIXME: replace with memmem()?
    const char* crlf = std::search(peek(), beginWrite(), kCRLF, kCRLF+2);
    return crlf == beginWrite() ? NULL : crlf;
  }

  const char* findCRLF(const char* start) const//在start~writeIndex区间寻找kCRLF
  {
    assert(peek() <= start);
    assert(start <= beginWrite());
    // FIXME: replace with memmem()?
    const char* crlf = std::search(start, beginWrite(), kCRLF, kCRLF+2);
    return crlf == beginWrite() ? NULL : crlf;
  }

  const char* findEOL() const//在readIndex~writeIndex区间寻找'\n'
  {
    const void* eol = memchr(peek(), '\n', readableBytes());
    return static_cast<const char*>(eol);
  }

  const char* findEOL(const char* start) const
  {
    assert(peek() <= start);
    assert(start <= beginWrite());
    const void* eol = memchr(start, '\n', beginWrite() - start);
    return static_cast<const char*>(eol);
  }

  // retrieve returns void, to prevent
  // string str(retrieve(readableBytes()), readableBytes());
  // the evaluation of two functions are unspecified
  void retrieve(size_t len)//回收len个字节的数据(可读数据)
  {
    assert(len <= readableBytes());
    if (len < readableBytes())
    {
      readerIndex_ += len;
    }
    else
    {
      retrieveAll();
    }
  }

  void retrieveUntil(const char* end)//回收readINdex~len区间的数据
  {
    assert(peek() <= end);
    assert(end <= beginWrite());
    retrieve(end - peek());
  }
  //回收相应类型大小的数据
  void retrieveInt64()
  {
    retrieve(sizeof(int64_t));
  }

  void retrieveInt32()
  {
    retrieve(sizeof(int32_t));
  }

  void retrieveInt16()
  {
    retrieve(sizeof(int16_t));
  }

  void retrieveInt8()
  {
    retrieve(sizeof(int8_t));
  }

  void retrieveAll()//回收所有空间
  {
    readerIndex_ = kCheapPrepend;
    writerIndex_ = kCheapPrepend;
  }

  string retrieveAllAsString()//返回缓冲区所有剩余的数据
  {
    return retrieveAsString(readableBytes());
  }

  string retrieveAsString(size_t len)//回收len大小的数据,并将这段数据返回
  {
    assert(len <= readableBytes());
    string result(peek(), len);
    retrieve(len);
    return result;
  }

  //返回StringPiece类型,该类保存一个char*指针,并保存len长度,并提供一些基础方法(可以理解为低配版std::string)
  //保存
  StringPiece toStringPiece() const
  {
    return StringPiece(peek(), static_cast<int>(readableBytes()));
  }

  void append(const StringPiece& str)
  {
    append(str.data(), str.size());
  }

  void append(const char* /*restrict*/ data, size_t len)
  {
    ensureWritableBytes(len);//确保有可写字节大小的空间
    std::copy(data, data+len, beginWrite());//将追加数据加入缓冲区
    hasWritten(len);//更新writerIndex_
  }

  void append(const void* /*restrict*/ data, size_t len)
  {
    append(static_cast<const char*>(data), len);
  }

  void ensureWritableBytes(size_t len)//确保有可写字节大小的空间
  {
  	//如果可写空间大于len则什么也不干,小于则调整buffer
    if (writableBytes() < len)
    {
      makeSpace(len);
    }
    assert(writableBytes() >= len);
  }

  char* beginWrite()//writeIndex
  { return begin() + writerIndex_; }

  const char* beginWrite() const//writeIndex
  { return begin() + writerIndex_; }

  void hasWritten(size_t len)//writerIndex_追加移动len个字节
  {
    assert(len <= writableBytes());
    writerIndex_ += len;
  }

  void unwrite(size_t len)//writerIndex_减少移动len个字节
  {
    assert(len <= readableBytes());
    writerIndex_ -= len;
  }

  ///
  /// Append int64_t using network endian
  ///

  //将类型大小的数据转成网络字节数(大端)后放入缓冲区
  void appendInt64(int64_t x)
  {
    int64_t be64 = sockets::hostToNetwork64(x);
    append(&be64, sizeof be64);
  }

  ///
  /// Append int32_t using network endian
  ///
  void appendInt32(int32_t x)
  {
    int32_t be32 = sockets::hostToNetwork32(x);
    append(&be32, sizeof be32);
  }

  void appendInt16(int16_t x)
  {
    int16_t be16 = sockets::hostToNetwork16(x);
    append(&be16, sizeof be16);
  }

  void appendInt8(int8_t x)
  {
    append(&x, sizeof x);
  }

  ///
  /// Read int64_t from network endian
  ///
  /// Require: buf->readableBytes() >= sizeof(int32_t)
  //在缓冲区中读Intxx类型大小的数据,转换为主机字节序,并调整缓冲区的下标,然后返回数据
  int64_t readInt64()
  {
    int64_t result = peekInt64();
    retrieveInt64();
    return result;
  }

  ///
  /// Read int32_t from network endian
  ///
  /// Require: buf->readableBytes() >= sizeof(int32_t)
  int32_t readInt32()
  {
    int32_t result = peekInt32();
    retrieveInt32();
    return result;
  }

  int16_t readInt16()
  {
    int16_t result = peekInt16();
    retrieveInt16();
    return result;
  }

  int8_t readInt8()
  {
    int8_t result = peekInt8();
    retrieveInt8();
    return result;
  }

  ///
  /// Peek int64_t from network endian
  ///
  /// Require: buf->readableBytes() >= sizeof(int64_t)
  //在缓冲区中读Intxx类型大小的数据,转换为主机字节序,然后返回数据
  int64_t peekInt64() const
  {
    assert(readableBytes() >= sizeof(int64_t));
    int64_t be64 = 0;
    ::memcpy(&be64, peek(), sizeof be64);
    return sockets::networkToHost64(be64);
  }

  ///
  /// Peek int32_t from network endian
  ///
  /// Require: buf->readableBytes() >= sizeof(int32_t)
  int32_t peekInt32() const
  {
    assert(readableBytes() >= sizeof(int32_t));
    int32_t be32 = 0;
    ::memcpy(&be32, peek(), sizeof be32);
    return sockets::networkToHost32(be32);
  }

  int16_t peekInt16() const
  {
    assert(readableBytes() >= sizeof(int16_t));
    int16_t be16 = 0;
    ::memcpy(&be16, peek(), sizeof be16);
    return sockets::networkToHost16(be16);
  }

  int8_t peekInt8() const
  {
    assert(readableBytes() >= sizeof(int8_t));
    int8_t x = *peek();
    return x;
  }

  ///
  /// Prepend int64_t using network endian
  ///
  //转换为网络字节序,在缓冲区中读Intxx类型大小的数据,并调整缓冲区的下标,然后返回数据
  //将Intxx类型大小的数据转换为网络字节序,然后以前插的方式加入缓冲区
  void prependInt64(int64_t x)
  {
    int64_t be64 = sockets::hostToNetwork64(x);
    prepend(&be64, sizeof be64);
  }

  ///
  /// Prepend int32_t using network endian
  ///
  void prependInt32(int32_t x)
  {
    int32_t be32 = sockets::hostToNetwork32(x);
    prepend(&be32, sizeof be32);
  }

  void prependInt16(int16_t x)
  {
    int16_t be16 = sockets::hostToNetwork16(x);
    prepend(&be16, sizeof be16);
  }

  void prependInt8(int8_t x)
  {
    prepend(&x, sizeof x);
  }

  void prepend(const void* /*restrict*/ data, size_t len)//以前插的方式加入缓冲区,并调整下标
  {
    assert(len <= prependableBytes());
    readerIndex_ -= len;
    const char* d = static_cast<const char*>(data);
    std::copy(d, d+len, begin()+readerIndex_);
  }

  //可以抽象理解为将buffer_修改为std::max(kInitialSize(1024),readableBytes()+reserve)大小的空间
  void shrink(size_t reserve)
  {
    // FIXME: use vector::shrink_to_fit() in C++ 11 if possible.
    Buffer other;
    other.ensureWritableBytes(readableBytes()+reserve);//保证other拥有buffer_未读取数据的大小加上reserve预留空间大小的容量
    other.append(toStringPiece());//将buffer_的数据追加到other
    swap(other);//调用swap与buffer_交换
  }

  size_t internalCapacity() const//返回vector实际占用的容量
  {
    return buffer_.capacity();
  }

  /// Read data directly into buffer.
  ///
  /// It may implement with readv(2)
  /// @return result of read(2), @c errno is saved
  ssize_t readFd(int fd, int* savedErrno);

 private:

  char* begin()
  { return &*buffer_.begin(); }

  const char* begin() const
  { return &*buffer_.begin(); }

  void makeSpace(size_t len)
  {
  	//	可写空间		+	  已读空间 ==除去缓冲区未读数据外的空间大小
  	//len(需要的空间大小)+kCheapPrepend(8字节预留内存)
  	//小于则直接resize,大于则将数据移到前端
    if (writableBytes() + prependableBytes() < len + kCheapPrepend)//
    {
      // FIXME: move readable data
      buffer_.resize(writerIndex_+len);
    }
    else
    {
      // move readable data to the front, make space inside buffer
      assert(kCheapPrepend < readerIndex_);
      size_t readable = readableBytes();
      std::copy(begin()+readerIndex_,//将可读数据移动到前端,在缓冲区内部腾出空间
                begin()+writerIndex_,
                begin()+kCheapPrepend);
      readerIndex_ = kCheapPrepend;
      writerIndex_ = readerIndex_ + readable;
      assert(readable == readableBytes());
    }
  }

 private:
  std::vector<char> buffer_;
  size_t readerIndex_;
  size_t writerIndex_;

  static const char kCRLF[];
};

}  // namespace net
}  // namespace muduo

#endif  // MUDUO_NET_BUFFER_H

buffer.cc

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//

#include "muduo/net/Buffer.h"

#include "muduo/net/SocketsOps.h"

#include <errno.h>
#include <sys/uio.h>

using namespace muduo;
using namespace muduo::net;

const char Buffer::kCRLF[] = "\r\n";

const size_t Buffer::kCheapPrepend;
const size_t Buffer::kInitialSize;

ssize_t Buffer::readFd(int fd, int* savedErrno)
{
  // saved an ioctl()/FIONREAD call to tell how much to read
  char extrabuf[65536];
  struct iovec vec[2];
  const size_t writable = writableBytes();
  vec[0].iov_base = begin()+writerIndex_;
  vec[0].iov_len = writable;
  vec[1].iov_base = extrabuf;
  vec[1].iov_len = sizeof extrabuf;
  // when there is enough space in this buffer, don't read into extrabuf.
  // when extrabuf is used, we read 128k-1 bytes at most.
  //1.如果buffer_::size大于extrabuf::size,那我们则只用buffer_存取数据
  //2.如果小于,则两块内存都使用,根据下标顺序先将数据写入buffer_,再将数据写入writable
  //在这个表达式下,一次性最多能读取的数据大小为writable==65535,65535+65536=131071,也就是128k-1的大小,而一次性最少的空间为extrabuf(64k)+buffer_(初始化最少空间为1k+8byte)
  const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;
  const ssize_t n = sockets::readv(fd, vec, iovcnt);
  if (n < 0)
  {
    *savedErrno = errno;
  }
  //如果读取的数据小于writable,则直接更新buffer_下标就行了,
  //因为上述无论是第一种情况还是第二种情况,数据都是先写入buffer_
  else if (implicit_cast<size_t>(n) <= writable)
  {
    writerIndex_ += n;
  }
  //如果是第二种情况则直接把下标设置在末尾,然后调用append函数并将extrabuf的数据写入buffer_(内部会调整buffer_大小并追加数据)
  else
  {
    writerIndex_ = buffer_.size();
    append(extrabuf, n - writable);
  }
  // if (n == writable + sizeof extrabuf)
  // {
  //   goto line_30;
  // }
  return n;
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1147151.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

msvcp140.dll丢失的正确解决方法

在使用电脑中我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp140.dll丢失”。这个错误通常会导致某些应用程序无法正常运行。为了解决这个问题&#xff0c;我们需要采取一些措施来修复丢失的msvcp140.dll文件。本文将介绍6个不同的解决方法&#xff0c;帮助读者解决…

java lombok

Lombok是一个实用的Java类库&#xff0c;可以通过简单的注解来简化和消除一些必须有但显得很臃肿的Java代码。 通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法&#xff0c;并可以自动化生成日志变量&#xff0c;简化java开发、提高效率&#…

AI:39-基于深度学习的车牌识别检测

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…

不用动脑子的技巧!已知二叉树的前序中序遍历,确定二叉树/求后序遍历

根据前&#xff08;后&#xff09;序、中序&#xff0c;确定二叉树&#xff0c;高妙的方法&#xff01;&#xff01;&#xff01; 二叉树的前中后序遍历⏩巧妙的方法&#xff01;根据前序遍历和中序遍历&#xff0c;确定二叉树例题1例题2 根据后序遍历和中序遍历&#xff0c;确…

CS224W3.2——随机游走(Random Walk)

上一文中说道定义节点相似度函数的时候使用Random Walk方法&#xff1a; CS224W3.1——节点Embedding 这节课来说一下Random Walk方法。在这篇中&#xff0c;我们来看一个更有效的相似函数——在图上随机游走的节点共现的概率。我们介绍随机游走背后的直觉&#xff0c;我们将…

中电文思海辉:塑造全球AI能力,持续强化诸多行业战略

【科技明说 &#xff5c; 重磅专题】 中电文思海辉以前就是叫文思海辉&#xff0c; 这是由之前两家上市软件外包公司文思信息和海辉软件合并而来&#xff0c;2018年当时各自股票以1:1的比例进行整合&#xff0c;双方股东各持有新公司50%的股权&#xff0c;合并后新公司名称为文…

私有云:【4】Esxi安装Server2012R2

私有云&#xff1a;【4】Esxi安装Server2012R2 1、使用Esxi安装虚拟机2、启动虚拟机3、安装必要服务及打补丁 1、使用Esxi安装虚拟机 选择esxi虚拟机挂在Win2012的镜像iso 使用Esxi客户端新建虚拟机 设置虚拟机名称及系统作为模板&#xff0c;如图所示 选择数据存储&#xff0…

mysql 增删改查基础命令

数据库是企业的重要信息资产&#xff0c;在使用数据库时&#xff0c;要注意(查和增,无所谓,但是删和改,要谨慎! ) 数据库管理系统(DBMS) :实现对数据的有效组织&#xff0c;管理和存取的系统软件 mysgl 数据库是一个系统&#xff0c; 是一个人机系统&#xff0c;硬件, gs,数据库…

软件设计师做题技巧(下午题)

第一题 数据流图 名词解释外部实体系统外部现实世界存在的物体 矩形表示数据存储一般都是数据库表名 矩形表示数据流数据如何在系统中流动和传输加工/数据处理系统的计算或者操作 圆角矩阵表示数据流图等级数据流图等级是指所涉及的数据项和处理的复杂程度 采用结构化语言对x…

【5G PHY】5G SS/PBCH块介绍(二)

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

链式存储方式下字符串的replace(S,T1,T2)运算

链式存储方式下字符串的replace运算 ⭐️题目⭐️思路⭐️代码✨定义结点✨打印字符串函数✨计算字符串函数✨初始化字符串函数✨代码解读✨字符串替换函数✨字符串替换函数解读✨ 主函数✨完整代码 实现在链式存储下字符串的replace(S,T1,T2)&#xff0c;来自课本习题的一道题…

[Linux]线程池

[Linux]线程池 文章目录 [Linux]线程池线程池的概念线程池的优点线程池的应用场景线程池的实现 线程池的概念 线程池是一种线程使用模式。线程池是一种特殊的生产消费模型&#xff0c;用户作为生产者&#xff0c;线程池作为消费者和缓冲区。 线程过多会带来调度开销&#xff0c…

【计算机视觉】对极几何

文章目录 一、极线约束&#xff08;Epipolar Constraint&#xff09;二、相机标定过的情况三、相机没有标定过的情况四、八点算法&#xff08;eight-point algorithm&#xff09; 我的《计算机视觉》系列参考UC Berkeley的CS180课程&#xff0c;PPT可以在课程主页看到。 在上一…

进行商城的测试用例设计思路是什么?

进行商城的测试用例设计时&#xff0c;可以考虑以下思路&#xff1a; 1. 功能测试&#xff1a;测试商城的基本功能是否正常工作&#xff0c;包括用户注册、登录、浏览商品、搜索商品、添加商品到购物车、下单、支付等。 2. 数据验证测试&#xff1a;验证商城中的数据是否正确…

深入浅出认识Kubernetes

用来管理容器&#xff0c;容器编排工具 容器化有助于打包软件来实现这些目标&#xff0c;从而使应用程序可以轻松快速地发布和更新&#xff0c;而无需停机。Kubernetes可帮助您确保那些容器化的应用程序在所需的位置和时间运行&#xff0c;并帮助他们找到工作所需的资源和工具。…

10阶杨辉三角

【任务需求】 定义一个函数&#xff0c;根据杨辉三角的数学概念&#xff0c;使用循环嵌套进行编写实现杨辉三角的关&#xff0c;并用for循环实现10阶杨辉三角&#xff0c;最后输出时需使10阶杨辉三角每行数字左右对称&#xff0c;按要求编写程序。 def triangle(rows):triang…

将数据文件,控制文件,日志文件分别放在不同的目录下,且数据库正常启动

一、定位数据文件、控制文件、日志文件的位置 注意&#xff1a;后序需要用到这些文件的位置&#xff0c;可以在查询完毕之后先截图保存 1.以管理员身份登录数据库 sqlplus / as sysdba2.查找数据文件位置 SELECT name FROM v$datafile;3.查找控制文件位置 SELECT name FROM …

设计模式:享元模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)

上一篇《原型模式》 下一篇《责任链模式》 简介&#xff1a; 享元模式&#xff0c;它是一种结构型设计模式&#xff0c;旨在有效地支持大量细粒度的对象共享&#xff0c;通过共享对象来减少内存消耗和…

【C++】缺省参数及函数重载

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1. 缺省参数1.1 缺省…

常见持久层框架赏析,到底是什么让你选择 MyBatis?

在绝大多数在线应用场景中&#xff0c;数据是存储在关系型数据库中的&#xff0c;当然&#xff0c;有特殊要求的场景中&#xff0c;我们也会将其他持久化存储&#xff08;如 ElasticSearch、HBase、MongoDB 等&#xff09;作为辅助存储。但不可否认的是&#xff0c;关系型数据库…