环形队列
前言
循环队列:队列有着先入先出的特性。但是对于队列如果删除队头以后剩下的空间将不会被释放,又由于队列只能由队尾插入这就导致被删除部分的空间被浪费。
解决这个问题就是循环队列。循环队列顾名思义就是将队列串起来形成一个类似与环的结构。
概念示意图
内存中不存在环形数据结构,均由基础结构实现逻辑上的闭环效果
环形队列与普通队列的区别
- front头部指针:
-
一般队列:front头部指针初始值为-1,从队列取数据时,该值依次递增,指向的元素即待取出的数据,而队列的头部数据所在的指针位置为front+1。当front=maxSize-1时,队列最后一个数据取出,此时队列为空。
-
环形队列:front头部指针初始值为0,指向的元素既是队列的头部数据也是待取出的数据。从队列取数据时,因逻辑上的闭环,指针可能再次回到前面的位置,不能单一递增处理,需通过取模来重新计算指针的值。
- rear尾部指针
-
一般队列:rear尾部指针初始值为-1,队列添加数据时,该值依次递增,当rear=maxSize-1时,队列满,无法再添加数据。
-
环形队列:rear尾部指针初始值为0,指向待添加数据的位置,队列添加数据时,因逻辑上的闭环,指针可能再次回到前面的位置,不能单一递增处理,会出现角标越界异常,需通过取模来重新计算指针的值。
- 队列空的判断逻辑
-
一般队列:rear == front时,队列空。
-
环形队列:rear == front时,队列空。
- 队列满的判断逻辑
-
一般队列: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;
}