predixy/src/Buffer.cpp
2017-07-24 11:00:44 +08:00

382 lines
8.0 KiB
C++

/*
* predixy - A high performance and full features proxy for redis.
* Copyright (C) 2017 Joyield, Inc. <joyield.com@gmail.com>
* All rights reserved.
*/
#include "String.h"
#include "Logger.h"
#include "Buffer.h"
#include "IOVec.h"
int Buffer::Size = 4096 - sizeof(Buffer);
Buffer::Buffer():
mLen(0)
{
}
Buffer::Buffer(const Buffer& oth):
mLen(oth.mLen)
{
memcpy(mDat, oth.mDat, mLen);
}
Buffer::~Buffer()
{
SharePtr<Buffer> p = ListNode<Buffer, SharePtr<Buffer>>::next();
ListNode<Buffer, SharePtr<Buffer>>::reset();
while (p) {
if (p->count() == 1) {
p = p->next();
} else {
break;
}
}
}
Buffer& Buffer::operator=(const Buffer& oth)
{
if (this != &oth) {
mLen = oth.mLen;
memcpy(mDat, oth.mDat, mLen);
}
return *this;
}
Buffer* Buffer::append(const char* str)
{
return append(str, strlen(str));
}
Buffer* Buffer::append(const char* dat, int len)
{
if (len <= 0) {
return this;
}
Buffer* buf = this;
while (len > 0) {
if (len <= buf->room()) {
memcpy(buf->tail(), dat, len);
buf->use(len);
break;
} else {
int n = buf->room();
memcpy(buf->tail(), dat, n);
buf->use(n);
dat += n;
len -= n;
Buffer* nbuf = BufferAlloc::create();
buf->concat(nbuf);
buf = nbuf;
}
}
return buf;
}
Buffer* Buffer::fappend(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
char* dat = tail();
int len = room();
int n = vsnprintf(dat, len, fmt, ap);
va_end(ap);
if (n >= len) {
if (n > MaxBufFmtAppendLen) {
return nullptr;
}
char buf[MaxBufFmtAppendLen];
va_list aq;
va_start(aq, fmt);
n = vsnprintf(buf, sizeof(buf), fmt, aq);
va_end(aq);
return append(buf, n);
}
use(n);
return this;
}
Buffer* Buffer::vfappend(const char* fmt, va_list ap)
{
char* dat = tail();
int len = room();
va_list aq;
va_copy(aq, ap);
int n = vsnprintf(dat, len, fmt, ap);
if (n >= len) {
if (n > MaxBufFmtAppendLen) {
va_end(aq);
return nullptr;
}
char buf[MaxBufFmtAppendLen];
n = vsnprintf(buf, sizeof(buf), fmt, aq);
va_end(aq);
return append(buf, n);
}
use(n);
va_end(aq);
return this;
}
Segment::Segment()
{
}
Segment::Segment(BufferPtr beginBuf, int beginPos, BufferPtr endBuf, int endPos):
mBegin(beginBuf, beginPos),
mCur(beginBuf, beginPos),
mEnd(endBuf, endPos)
{
}
Segment::Segment(const Segment& oth):
mBegin(oth.mBegin),
mCur(oth.mCur),
mEnd(oth.mEnd)
{
}
Segment::Segment(Segment&& oth):
mBegin(oth.mBegin),
mCur(oth.mCur),
mEnd(oth.mEnd)
{
}
Segment::~Segment()
{
clear();
}
Segment& Segment::operator=(const Segment& oth)
{
if (this != &oth) {
mBegin = oth.mBegin;
mCur = oth.mCur;
mEnd = oth.mEnd;
}
return *this;
}
void Segment::clear()
{
mBegin.buf = nullptr;
mBegin.pos = 0;
mCur.buf = nullptr;
mCur.pos = 0;
mEnd.buf = nullptr;
mEnd.pos = 0;
}
Buffer* Segment::set(Buffer* buf, const char* dat, int len)
{
if (!buf) {
buf = BufferAlloc::create();
}
int pos = buf->length();
Buffer* nbuf = buf->append(dat, len);
mBegin.buf = buf;
mBegin.pos = pos;
mCur.buf = buf;
mCur.pos = pos;
mEnd.buf = nbuf;
mEnd.pos = nbuf->length();
return nbuf;
}
Buffer* Segment::fset(Buffer* buf, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
try {
return vfset(buf, fmt, ap);
} catch (...) {
va_end(ap);
throw;
}
return nullptr;
}
Buffer* Segment::vfset(Buffer* buf, const char* fmt, va_list ap)
{
if (!buf) {
buf = BufferAlloc::create();
}
int pos = buf->length();
Buffer* nbuf = buf->vfappend(fmt, ap);
mBegin.buf = buf;
mBegin.pos = pos;
mCur = mBegin;
mEnd.buf = nbuf;
mEnd.pos = nbuf->length();
return nbuf;
}
int Segment::length() const
{
Buffer* buf = mBegin.buf;
int pos = mBegin.pos;
int len = 0;
while (buf) {
if (buf == mEnd.buf) {
len += mEnd.pos - pos;
break;
} else {
len += buf->length() - pos;
}
buf = buf->next();
pos = 0;
}
return len;
}
bool Segment::get(const char*& dat, int& len) const
{
if (mCur.buf == mEnd.buf && mCur.pos >= mEnd.pos) {
dat = nullptr;
len = 0;
return false;
}
dat = mCur.buf->data() + mCur.pos;
len = (mCur.buf == mEnd.buf ? mEnd.pos : mCur.buf->length()) - mCur.pos;
return true;
}
int Segment::fill(IOVec* vecs, int len, bool& all) const
{
int n = 0;
BufferPos p = mCur;
all = false;
while (n < len && p != mEnd) {
vecs[n].dat = p.buf->data() + p.pos;
vecs[n].len = (p.buf == mEnd.buf ? mEnd.pos : p.buf->length()) - p.pos;
vecs[n].seg = const_cast<Segment*>(this);
vecs[n].buf = p.buf;
vecs[n].pos = p.pos;
vecs[n].req = nullptr;
++n;
if (p.buf == mEnd.buf) {
all = true;
break;
} else {
p.buf = p.buf->next();
p.pos = 0;
}
}
if (n < len && n == 0) {
all = true;
}
return n;
}
//caller must sure cnt < segment length
void Segment::cut(int cnt)
{
if (cnt > 0) {
while (cnt > 0 && mBegin.buf) {
BufferPtr buf = mBegin.buf;
int len = (buf == mEnd.buf ? mEnd.pos : buf->length()) - mBegin.pos;
if (len <= cnt) {
if (buf == mEnd.buf) {
mBegin.buf = nullptr;
mBegin.pos = 0;
mCur = mEnd = mBegin;
break;
}
cnt -= len;
mBegin.buf = buf->next();
mBegin.pos = 0;
if (mCur.buf == buf) {
mCur = mBegin;
}
} else {
mBegin.pos += cnt;
if (mCur.buf == mBegin.buf && mCur.pos < mBegin.pos) {
mCur.pos = mBegin.pos;
}
break;
}
}
}
}
//caller must sure cnt < segment length
void Segment::use(int cnt)
{
while (cnt > 0) {
if (mCur.buf == mEnd.buf) {
mCur.pos += cnt;
if (mCur.pos > mEnd.pos) {
mCur.pos = mEnd.pos;
}
break;
} else {
int n = mCur.buf->length() - mCur.pos;
if (n <= cnt) {
mCur.buf = mCur.buf->next();
mCur.pos = 0;
} else {
mCur.pos += cnt;
}
cnt -= n;
}
}
}
void Segment::seek(Buffer* buf, int pos, int cnt)
{
mCur.buf = buf;
mCur.pos = pos;
use(cnt);
}
int Segment::dump(char* dat, int len) const
{
int cnt = 0;
BufferPtr buf = mBegin.buf;
int pos = mBegin.pos;
bool end = false;
while (buf && !end) {
int num;
if (buf == mEnd.buf) {
num = mEnd.pos - pos;
end = true;
} else {
num = buf->length() - pos;
}
if (dat && len > 0) {
memcpy(dat, buf->data() + pos, len > num ? num : len);
dat += num;
len -= num;
}
cnt += num;
buf = buf->next();
pos = 0;
}
return cnt;
}
bool Segment::hasPrefix(const char* prefix) const
{
const char* p = prefix;
BufferPtr buf = mBegin.buf;
int pos = mBegin.pos;
bool end = false;
while (buf && !end) {
int last = buf->length();
if (buf == mEnd.buf) {
last = mEnd.pos;
end = true;
}
while (pos < last && *p) {
if (buf->data()[pos++] != *p++) {
return false;
}
}
buf = buf->next();
pos = 0;
}
return *p == '\0';
}