muduo源码剖析之Buffer缓冲区类

简介

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_tnamespace 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.6void 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 unspecifiedvoid 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则什么也不干,小于则调整bufferif (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_的数据追加到otherswap(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 savedssize_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 databuffer_.resize(writerIndex_+len);}else{// move readable data to the front, make space inside bufferassert(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 readchar 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.mzph.cn/news/121793.shtml

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

相关文章

城市群(Megalopolis)/城际(inter-city)OD相关研究即Open Access数据集调研

文章目录 1 城市群/城际OD定义2 理论模型与分析方法2.1 重力模型 Gravity Model2.2 干预机会模型 Intervening Opportunities Model2.3 辐射模型 Radiation Model 3 Issues related to OD flows3.1 OD Prediction3.2 OD Forecasting3.3 OD Construction3.4 OD Estimation 4 OD …

PHP简单实现预定义钩子和自定义钩子

在PHP中&#xff0c;钩子&#xff08;Hooks&#xff09;是一种机制&#xff0c;允许开发人员在特定的时机插入自定义代码。通过使用钩子&#xff0c;开发人员可以在应用程序的特定事件发生时执行自定义的功能或逻辑 钩子有两种类型&#xff1a;预定义钩子和自定义钩子。 预定…

Ubuntu安装docker,并换镜像源详细教程,建议收藏

文章目录 添加docker官方的GPG密钥将docker仓库添加到apt源安装docker检查docker换源 添加docker官方的GPG密钥 sudo apt-get updatesudo apt-get install ca-certificates curl gnupgsudo install -m 0755 -d /etc/apt/keyringscurl -fsSL https://download.docker.com/linux…

【axios】axios的基本使用

一、 Axios简介 1、 Axios是什么&#xff1f; Axios是一个基于promise的HTTP库&#xff0c;类似于jQuery的ajax&#xff0c;用于http请求。可以应用于浏览器端和node.js&#xff0c;既可以用于客户端&#xff0c;也可以用于node.js编写的服务端。 2.、Axios特性 支持Promis…

Groovy安装开发环境

准备下载GDK并安装环境变量,跟安装JDK一模一样 https://groovy.apache.org/download.html

安装使用vcpkg的简易教程

目录 1. 首先安装vcpkg2. 在vcpkg目录下运行bootstrap-vcpkg.bat 命令3. 接着vs进行集成4. 使用vcpkg搜索可用的包5.下载安装所需包6.下载安装完成 1. 首先安装vcpkg 使用git命令下载 git clone https://github.com/Microsoft/vcpkg.git如果下载失败可直接下载文件 (vcpkg-ma…

基于Pytest+Requests+Allure实现接口自动化测试!

一、整体结构 框架组成&#xff1a;pytestrequestsallure设计模式&#xff1a; 关键字驱动项目结构&#xff1a; 工具层&#xff1a;api_keyword/参数层&#xff1a;params/用例层&#xff1a;case/数据驱动&#xff1a;data_driver/数据层&#xff1a;data/逻辑层&#xff1a…

C++基础:函数模板

为了代码重用&#xff0c;代码必须是通用的&#xff1b;通用的代码就必须不受数据类型的限制。那么我们可以把数据类型改为一个设计参数&#xff0c;这种类型的程序设计称为参数化程序设计&#xff0c;软件模板有模板构造&#xff0c;包括函数模板和类模板。 函数模板可以用来…

设计模式(19)命令模式

一、介绍&#xff1a; 1、定义&#xff1a;命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求封装为一个对象&#xff0c;从而使你可以使用不同的请求对客户端进行参数化。命令模式还支持请求的排队、记录日志、撤销操作等功能。 2、组…

在spring boot+vue项目中@CrossOrigin 配置了允许跨域但是依然报错跨域,解决跨域请求的一次残酷经历

首先&#xff0c;说一下我们的项目情况&#xff0c;我们项目中后端有一个过滤器&#xff0c;如果必须要登录的接口路径会被拦下来检查&#xff0c;前端要传一个token&#xff0c;然后后端根据这个token来判断redis中这个用户是否已经登录。 if (request.getMethod().equals(&qu…

Azure云工作站上做Machine Learning模型开发 - 全流程演示

目录 本文内容先决条件从“笔记本”开始设置用于原型制作的新环境&#xff08;可选&#xff09;创建笔记本开发训练脚本迭代检查结果 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0…

Dataworks API:调取 MC 项目下所有表单

文章目录 前言Dataworks API 文档解读GetMetaDBTableList 接口文档 API 调试在线调试本地调试运行环境账密问题请求数据进一步处理 小结 前言 最近&#xff0c;我需要对公司的数据资产进行梳理&#xff0c;这其中便包括了Dataworks各个项目下的表单。这些表单&#xff0c;作为…

【CSS】伪类和伪元素

伪类 :hover&#xff1a;悬停active&#xff1a;激活focus&#xff1a;获取焦点:link&#xff1a;未访问&#xff08;链接&#xff09;:checked&#xff1a;勾选&#xff08;表单&#xff09;first-child&#xff1a;第一个子元素nth-child()&#xff1a;指定索引的子元素&…

『力扣刷题本』:移除链表元素

一、题目 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff1a; 输入&a…

MacOS系统电脑怎么彻底清理系统垃圾注册表App Cleaner可以深度清理吗

App Cleaner & Uninstaller 是一款适用于 Mac 操作系统的软件应用程序&#xff0c;允许用户轻松卸载不需要的应用程序、删除剩余文件和文件夹以及管理启动项。该应用程序会分析与您要删除的应用程序关联的文件&#xff0c;并帮助识别其所有组件&#xff0c;以便您可以一次将…

如何在Node.js中使用环境变量或命令行参数来设置HTTP爬虫ip?

首先&#xff0c;定义问题&#xff1a;在 Node.js 应用程序中&#xff0c;我们可以通过环境变量或命令行参数来设置HTTP爬虫ip&#xff0c;以便在发送请求时使用这些HTTP爬虫ip。 亲身经验&#xff1a;我曾经需要为一个项目设置HTTP爬虫ip&#xff0c;以便在发送请求时使用这些…

Controller接收Postman的raw参数时,属性值全部为空

Controller接收Postman的raw参数时&#xff0c;属性值全部为空 情景再现 在进行业务代码的编写过程中&#xff0c;使用Postman等工具调用Controller接口时&#xff0c;发现属性值全部为空后端代码如下&#xff1a; Requset对象为&#xff1a; public class QuerySkuRequest …

kali搭建docker

kali搭建docker 更新kali源 sudo apt update出错 更新一下密钥 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ED444FF07D8D0BF6安装docker sudo apt install docker.io -y #安装docker docker -v #docker版本 sudo systemctl status docker #查看docker…

深入理解Java中的转义字符

最近在学习《两周自制脚本语言》这本书&#xff0c;在词法分析的一些复杂的正则中用到了大量的转义字符’\&#xff0c;比如正则字符串中包含了这个部分\\\\\"你知道它是匹配什么的么&#xff1f; 反斜杠在字符串和正则表达式中都有特殊作用。今天让我们来深入理解一下Ja…

自监督学习应用

1 自监督学习 自监督学习主要是利用辅助任务&#xff08;pretext&#xff09;从大规模的无监督数据中挖掘自身的监督信息&#xff0c;通过这种构造的监督信息对网络进行训练&#xff0c;从而可以学习到对下游任务有价值的表征。&#xff08;也就是说自监督学习的监督信息不是人…