接收和发送缓冲区的设计

环形队列

前言

循环队列:队列有着先入先出的特性。但是对于队列如果删除队头以后剩下的空间将不会被释放,又由于队列只能由队尾插入这就导致被删除部分的空间被浪费。

解决这个问题就是循环队列。循环队列顾名思义就是将队列串起来形成一个类似与环的结构。

概念示意图

内存中不存在环形数据结构,均由基础结构实现逻辑上的闭环效果

img

环形队列与普通队列的区别

  1. front头部指针:
  • 一般队列:front头部指针初始值为-1,从队列取数据时,该值依次递增,指向的元素即待取出的数据,而队列的头部数据所在的指针位置为front+1。当front=maxSize-1时,队列最后一个数据取出,此时队列为空。

  • 环形队列:front头部指针初始值为0,指向的元素既是队列的头部数据也是待取出的数据。从队列取数据时,因逻辑上的闭环,指针可能再次回到前面的位置,不能单一递增处理,需通过取模来重新计算指针的值。

  1. rear尾部指针
  • 一般队列:rear尾部指针初始值为-1,队列添加数据时,该值依次递增,当rear=maxSize-1时,队列满,无法再添加数据。

  • 环形队列:rear尾部指针初始值为0,指向待添加数据的位置,队列添加数据时,因逻辑上的闭环,指针可能再次回到前面的位置,不能单一递增处理,会出现角标越界异常,需通过取模来重新计算指针的值。

  1. 队列空的判断逻辑
  • 一般队列:rear == front时,队列空。

  • 环形队列:rear == front时,队列空。

  1. 队列满的判断逻辑
  • 一般队列:rear = maxSize - 1时,队列满。

  • 环形队列:(rear + 1) % maxSize == front时,队列满。

设计图

在这里插入图片描述

源码

头文件

#pragma once

#include <algorithm>
#include <vector>
#include <string>
#include <string.h>

#include "../base/Platform.h"
#include "Sockets.h"
#include "Endian.h"
//#include <unistd.h>  // ssize_t


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:
		static const size_t kCheapPrepend = 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
		{
			return begin() + readerIndex_;
		}

        const char* findString(const char* targetStr) const
        {
            const char* found = std::search(peek(), beginWrite(), targetStr, targetStr + strlen(targetStr));
            return found == beginWrite() ? nullptr : found;
        }

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

		const char* findCRLF(const char* start) const
		{
            if (peek() > start)
                return nullptr;

            if (start > beginWrite())
                return nullptr;

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

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

		const char* findEOL(const char* start) const
		{
            if (peek() > start)
                return nullptr;

            if (start > beginWrite())
                return nullptr;

			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
		bool retrieve(size_t len)
		{
            if (len > readableBytes())
                return false;

			if (len < readableBytes())
			{
				readerIndex_ += len;
			}
			else
			{
				retrieveAll();
			}

            return true;
		}

		bool retrieveUntil(const char* end)
		{
            if (peek() > end)
                return false;

            if (end > beginWrite())
                return false;

			retrieve(end - peek());

            return true;
		}

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

		std::string retrieveAllAsString()
		{
			return retrieveAsString(readableBytes());;
		}

		std::string retrieveAsString(size_t len)
		{
            if (len > readableBytes())
                return "";

			std::string result(peek(), len);
			retrieve(len);
			return result;
		}

		std::string toStringPiece() const
		{
			return std::string(peek(), static_cast<int>(readableBytes()));
		}

		void append(const std::string& str)
		{
			append(str.c_str(), str.size());
		}

		void append(const char* /*restrict*/ data, size_t len)
		{
			//其实相当于把已有数据往前挪动
            ensureWritableBytes(len);
			std::copy(data, data + len, beginWrite());
			hasWritten(len);
		}

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

		bool ensureWritableBytes(size_t len)
		{
			//剩下的可写空间如果小于需要的空间len,则增加len长度个空间
            if (writableBytes() < len)
			{
				makeSpace(len);
			}

            return true;

            //if (writableBytes() >= len)
            //    return false;
		}

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

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

		bool hasWritten(size_t len)
		{
            if (len > writableBytes())
                return false;

			writerIndex_ += len;
            return true;
		}

		bool unwrite(size_t len)
		{
            if (len > readableBytes())
                return false;

			writerIndex_ -= len;
            return true;
		}

		///
		/// 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)
		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)
		int64_t peekInt64() const
		{
            if (readableBytes() < sizeof(int64_t))
                return -1;

			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
		{
            if (readableBytes() < sizeof(int32_t))
                return -1;

			int32_t be32 = 0;
			::memcpy(&be32, peek(), sizeof be32);
			return sockets::networkToHost32(be32);
		}

		int16_t peekInt16() const
		{
            if (readableBytes() < sizeof(int16_t))
                return -1;

			int16_t be16 = 0;
			::memcpy(&be16, peek(), sizeof be16);
			return sockets::networkToHost16(be16);
		}

		int8_t peekInt8() const
		{
            if (readableBytes() < sizeof(int8_t))
                return -1;

			int8_t x = *peek();
			return x;
		}

		///
		/// Prepend int64_t using network endian
		///
		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);
		}

		bool prepend(const void* /*restrict*/ data, size_t len)
		{
            if (len > prependableBytes())
                return false;

			readerIndex_ -= len;
			const char* d = static_cast<const char*>(data);
			std::copy(d, d + len, begin() + readerIndex_);
            return true;
		}

		void shrink(size_t reserve)
		{
			// FIXME: use vector::shrink_to_fit() in C++ 11 if possible.
			Buffer other;
			other.ensureWritableBytes(readableBytes() + reserve);
			other.append(toStringPiece());
			swap(other);
		}

		size_t internalCapacity() const
		{
			return buffer_.capacity();
		}

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

	private:

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

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

		void makeSpace(size_t len)
		{
			//kCheapPrepend为保留的空间
            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_);
                if (kCheapPrepend >= readerIndex_)
                    return;

				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[];
	};

}

源码文件

#include "Buffer.h"

#include "../base/Platform.h"
#include "Sockets.h"
#include "Callbacks.h"

using namespace net;

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

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

int32_t Buffer::readFd(int fd, int* savedErrno)
{
	// saved an ioctl()/FIONREAD call to tell how much to read
	char extrabuf[65536];
    const size_t writable = writableBytes();
#ifndef WIN32
	struct iovec vec[2];
	
	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.
	const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;
	const ssize_t n = sockets::readv(fd, vec, iovcnt);
#else
    const int32_t n = sockets::read(fd, extrabuf, sizeof(extrabuf));
#endif
	if (n <= 0)
	{
#ifdef WIN32
        *savedErrno = ::WSAGetLastError();
#else
		*savedErrno = errno;
#endif
	}
	else if (implicit_cast<size_t>(n) <= writable)
	{
#ifdef WIN32
        //Windows平台需要手动把接收到的数据加入buffer中,Linux平台已经在 struct iovec 中指定了缓冲区写入位置
        append(extrabuf, n);
#else
        writerIndex_ += n;
#endif
	}
	else
	{
#ifdef WIN32
		//Windows平台直接将所有的字节放入缓冲区去
        append(extrabuf, n);
#else
        //Linux平台把剩下的字节补上去
        writerIndex_ = buffer_.size();
        append(extrabuf, n - writable);
#endif
	}
	// if (n == writable + sizeof extrabuf)
	// {
	//   goto line_30;
	// }
	return n;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值