webserver--Buffer类实现内存缓冲区读写(全代码)

简介: webserver--Buffer类实现内存缓冲区读写(全代码)

  C++类Buffer的实现,用于处理内存缓冲区的读写操作。Buffer类封装了一个固定大小的字符数组,提供了一系列方法来管理这个缓冲区中的数据。

       一般的Buffer缓冲区实现是个什么过程呢?



初始化:首先,需要初始化一个 Buffer 对象,用于存储待处理的数据。可以指定缓冲区的大小以及其他相关参数。


写入数据:一旦 Buffer 对象被初始化,可以通过不同的方法向其写入数据。这些方法通常包括直接写入字符串、从文件读取数据写入、从另一个缓冲区复制数据等。


读取数据:一旦数据被写入缓冲区,可以使用相应的读取方法从缓冲区中读取数据。这些方法可能包括读取指定长度的数据、读取直到特定分隔符的数据、以及将数据转换为不同的格式等。


处理数据:读取到数据后,可以对其进行各种处理,例如解析 HTTP 请求、执行业务逻辑、或者转发数据给其他模块。


清空缓冲区:在数据被处理完毕后,可以选择清空缓冲区,以释放内存空间,并准备接收新的数据。


异常处理:在读写过程中可能会出现各种异常情况,例如缓冲区溢出、读取到不完整的数据等,需要相应的异常处理机制来处理这些情况,以确保系统的稳定性和可靠性。


几个重要的函数 :


构造函数 Buffer::Buffer(int initBuffSize):


初始化缓冲区大小为initBuffSize。

设置读位置readPos_和写位置writePos_为0,表示缓冲区开始时是空的。

Buffer::EnsureWriteable(size_t len):


确保缓冲区有足够的空间写入len个字节的数据,如果没有,则通过调整缓冲区大小或重新排列数据来提供空间。

Buffer::ReadFd(int fd, int* saveErrno):


从文件描述符fd读取数据到缓冲区,如果读取成功,更新写位置。

Buffer::WriteFd(int fd, int* saveErrno):


将缓冲区中的数据写入到文件描述符fd。

Buffer::BeginPtr_() 和 Buffer::BeginPtr_() const:


返回指向缓冲区开始的指针。

Buffer::MakeSpace_(size_t len):


如果需要的空间大于当前可写空间和可前置空间的总和,重新分配缓冲区大小;否则,将可读数据向前移动,为新数据腾出空间

完整代码:(太不容易了,点个赞呗,可以关注一下公众号和视频号^_^)

#include "buffer.h"
 
// 构造函数,初始化缓冲区大小、读位置和写位置
Buffer::Buffer(int initBuffSize) : buffer_(initBuffSize), readPos_(0), writePos_(0) {}
 
// 返回可读的字节数
size_t Buffer::ReadableBytes() const {
    return writePos_ - readPos_;
}
 
// 返回可写的字节数
size_t Buffer::WritableBytes() const {
    return buffer_.size() - writePos_;
}
 
// 返回可前置的字节数
size_t Buffer::PrependableBytes() const {
    return readPos_;
}
 
// 返回指向当前读位置的指针
const char* Buffer::Peek() const {
    return BeginPtr_() + readPos_;
}
 
// 移动读位置len个字节
void Buffer::Retrieve(size_t len) {
    assert(len <= ReadableBytes());
    readPos_ += len;
}
 
// 移动读位置,直到达到end指向的位置
void Buffer::RetrieveUntil(const char* end) {
    assert(Peek() <= end);
    Retrieve(end - Peek());
}
 
// 清空整个缓冲区,并将读位置和写位置重置为0
void Buffer::RetrieveAll() {
    bzero(&buffer_[0], buffer_.size());
    readPos_ = 0;
    writePos_ = 0;
}
 
// 将当前可读的数据转换为字符串,并清空缓冲区
std::string Buffer::RetrieveAllToStr() {
    std::string str(Peek(), ReadableBytes());
    RetrieveAll();
    return str;
}
 
// 返回指向当前写位置的只读指针
const char* Buffer::BeginWriteConst() const {
    return BeginPtr_() + writePos_;
}
 
// 返回指向当前写位置的可写指针
char* Buffer::BeginWrite() {
    return BeginPtr_() + writePos_;
}
 
// 移动写位置len个字节,表示写入了len个字节的数据
void Buffer::HasWritten(size_t len) {
    writePos_ += len;
}
 
// 将字符串str追加到缓冲区末尾
void Buffer::Append(const std::string& str) {
    Append(str.data(), str.length());
}
 
// 将数据data的len个字节追加到缓冲区末尾
void Buffer::Append(const void* data, size_t len) {
    assert(data);
    Append(static_cast<const char*>(data), len);
}
 
// 将字符串str的len个字节追加到缓冲区末尾
void Buffer::Append(const char* str, size_t len) {
    assert(str);
    EnsureWriteable(len);
    std::copy(str, str + len, BeginWrite());
    HasWritten(len);
}
 
// 将另一个Buffer对象buff中的数据追加到当前缓冲区
void Buffer::Append(const Buffer& buff) {
    Append(buff.Peek(), buff.ReadableBytes());
}
 
// 确保缓冲区有足够的空间写入len个字节的数据
void Buffer::EnsureWriteable(size_t len) {
    if(WritableBytes() < len) {
        MakeSpace_(len);
    }
    assert(WritableBytes() >= len);
}
 
// 从文件描述符fd读取数据到缓冲区
ssize_t Buffer::ReadFd(int fd, int* saveErrno) {
    char buff[65535];
    struct iovec iov[2];
    const size_t writable = WritableBytes();
    iov[0].iov_base = BeginPtr_() + writePos_;
    iov[0].iov_len = writable;
    iov[1].iov_base = buff;
    iov[1].iov_len = sizeof(buff);
 
    const ssize_t len = readv(fd, iov, 2);
    if(len < 0) {
        *saveErrno = errno;
    }
    else if(static_cast<size_t>(len) <= writable) {
        writePos_ += len;
    }
    else {
        writePos_ = buffer_.size();
        Append(buff, len - writable);
    }
    return len;
}
 
// 将缓冲区中的数据写入到文件描述符fd
ssize_t Buffer::WriteFd(int fd, int* saveErrno) {
    size_t readSize = ReadableBytes();
    ssize_t len = write(fd, Peek(), readSize);
    if(len < 0) {
        *saveErrno = errno;
        return len;
    } 
    readPos_ += len;
    return len;
}
 
// 返回指向缓冲区开始的指针
char* Buffer::BeginPtr_() {
    return &*buffer_.begin();
}
 
// 返回指向缓冲区开始的只读指针
const char* Buffer::BeginPtr_() const {
    return &*buffer_.begin();
}
 
// 如果需要的空间大于当前可写空间和可前置空间的总和,重新分配缓冲区大小;
// 否则,将可读数据向前移动,为新数据腾出空间
void Buffer::MakeSpace_(size_t len) {
    if(WritableBytes() + PrependableBytes() < len) {
        buffer_.resize(writePos_ + len + 1);
    } 
    else {
        size_t readable = ReadableBytes();
        std::copy(BeginPtr_() + readPos_, BeginPtr_() + writePos_, BeginPtr_());
        readPos_ = 0;
        writePos_ = readPos_ + readable;
        assert(readable == ReadableBytes());
    }
}
相关文章
|
8天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
30 4
|
10天前
|
存储 Java 程序员
结构体和类的内存管理方式在不同编程语言中的表现有何异同?
不同编程语言中结构体和类的内存管理方式既有相似之处,又有各自的特点。了解这些异同点有助于开发者在不同的编程语言中更有效地使用结构体和类来进行编程,合理地管理内存,提高程序的性能和可靠性。
19 3
|
12天前
|
存储 缓存 Java
结构体和类在内存管理方面的差异对程序性能有何影响?
【10月更文挑战第30天】结构体和类在内存管理方面的差异对程序性能有着重要的影响。在实际编程中,需要根据具体的应用场景和性能要求,合理地选择使用结构体或类,以优化程序的性能和内存使用效率。
|
12天前
|
存储 缓存 算法
结构体和类在内存管理方面有哪些具体差异?
【10月更文挑战第30天】结构体和类在内存管理方面的差异决定了它们在不同的应用场景下各有优劣。在实际编程中,需要根据具体的需求和性能要求来合理选择使用结构体还是类。
|
21天前
|
存储 JavaScript 前端开发
如何优化代码以避免闭包引起的内存泄露
本文介绍了闭包引起内存泄露的原因,并提供了几种优化代码的策略,帮助开发者有效避免内存泄露问题,提升应用性能。
|
1月前
|
存储 缓存 固态存储
|
1月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(二)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
1月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(三)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
1月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(一)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
3月前
|
存储 缓存 JSON
一行代码,我优化掉了1G内存占用
这里一行代码,指的是:String.intern()的调用,为了调用这一行代码,也写了几十行额外的代码。