缓冲区实现

发布时间:2022-06-27 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了缓冲区实现脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

代码来[^1] [^]:参考代码

buffer.h

#ifndef BUFFER_H
#define BUFFER_H

/*
网上没有找到很好的vector<char>和string区别
姑且认为和Qt的QByteArray和QString的关系一样,
一个是用来读写二进制流,一个是用来操作字符串的

见 陈硕书 7.4.3
Buffer:
缓冲
_wrITePos //读入数据到Buffer后的下标
_readPos  //可读数据的起始下标
*/

#include <atomic>
#include <cassert>
#include <string>
#include <Sys/uio.h> //readv
#include <unistd.h>  //write
#include <vector>

class Buffer
{
public:
  Buffer(int initBuffsize = 1024);
  ~Buffer() = default;

  size_t WritableBytes() const;
  size_t ReadableBytes() const;
  size_t PRePEndableBytes() const;

  const char* Peek() const;
  void EnsureWriteable(size_t len);
  void HasWritten(size_t len);

  void Retrieve(size_t len);
  void RetrieveUntil(const char* end);
  void RetrieveAll(const char* end);

  std::string RetrieveAllToStr();

  const char* BeginWriteConst() const;
  char* BeginWrite();

  void Append(const std::string&amp; str);
  void Append(const char* str, size_t len);
  void Append(const void* data, size_t len);
  void Append(const Buffer& buff);

  ssize_t ReaDFd(int fd, int* Errno);
  ssize_t WriteFd(int fd, int* Errno);

  std::vector<char> _buffer;

private:
  char* _BeginPtr();
  const char* _BeginPtr() const;
  void _MakeSpace(size_t len);

  // vector自动增长(会重新分配内存)
  // 整数下标,非指向_buffer的指针
  std::atomic<std::size_t> _readPos;
  std::atomic<size_t> _writePos;
};

#endif // BUFFER_H

buffer.cpp

#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;
}

void
Buffer::Retrieve(size_t len)
{
  //设置当前已读位置
  assert(len <= ReadableBytes());
  _readPos += len;
}

/*
end:   传入的读到的终点位置
brief: 设置新的_readPos(已读的位置)
*/

void
Buffer::RetrieveUntil(const char* end)
{
  assert(Peek() <= end);
  Retrieve(end - Peek());
}

std::string
Buffer::RetrieveAllToStr()
{
  //当前指针位置,剩余可读长度
  std::string str(Peek(), ReadableBytes());
  return str;
}

void
Buffer::Append(const std::string& str)
{
  Append(str.data(), str.length());
}

void
Buffer::Append(const void* data, size_t len)
{
  assert(data);
  Append(static_cast<const char*>(data), len);
}

void
Buffer::Append(const Buffer& buff)
{
  Append(buff.Peek(), buff.ReadableBytes());
}

void
Buffer::Append(const char* str, size_t len)
{
  assert(str);
  //保证buffer长度够 申请空间
  EnsureWriteable(len);
  std::copy(str, str + len, BeginWrite());
  HasWritten(len); //更新当前可写文件下标
}

void
Buffer::EnsureWriteable(size_t len)
{
  //确定可写的空间足够
  if (WritableBytes() < len) {
    _MakeSpace(len);
  }
  assert(WritableBytes() >= len);
}

const char*
Buffer::BeginWriteConst() const
{
  return _BeginPtr() + _writePos;
}

char*
Buffer::BeginWrite()
{
  return _BeginPtr() + _writePos;
}

void
Buffer::HasWritten(size_t len)
{
  //设置已写指针下标位置
  _writePos += len;
}

//把数据读到(写到)_buff
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; //把数据读到_buffer剩余长度
  iov[1].iov_base = buff;
  iov[1].iov_len = sizeof(buff); //读取全部的数据到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); // writable被读到_buff了
  }
  return len;
}

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)
{
  //剩余可写写 + 预占的大小 < len
  if (WritableBytes() + PrependableBytes() < len) {
    _buffer.resize(_writePos + len + 1);
  } else {
    size_t readable = ReadableBytes(); //_w - _r
    //起始 + 已读 ==> 复制到前面已读的空间
    //可以不用分配新的内存
    std::copy(_BeginPtr() + _readPos, _BeginPtr() + _writePos, _BeginPtr());
    _readPos = 0;
    //下一个可以写的位置
    _writePos = /*_readPos + */ readable;
    assert(readable == ReadableBytes());
  }
}

脚本宝典总结

以上是脚本宝典为你收集整理的缓冲区实现全部内容,希望文章能够帮你解决缓冲区实现所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。