Reactor/lib/Buffer.hpp
2025-06-27 18:01:00 -05:00

263 lines
5.8 KiB
C++

#pragma once
#include "Utilities.hpp"
#include <sys/uio.h>
#include <unistd.h>
#include <vector>
#include <algorithm>
#include <cassert>
namespace reactor {
class Buffer : public NonCopyable
{
private:
std::vector<char> buffer_;
size_t readIndex_;
size_t writeIndex_;
size_t initialCap_;
static constexpr size_t kBufferOffset = 8;
public:
static constexpr size_t kInitialSize = 1024;
explicit Buffer(size_t initialSize = kInitialSize)
: buffer_(initialSize + kBufferOffset), readIndex_(kBufferOffset),
writeIndex_(kBufferOffset), initialCap_(initialSize)
{
LOG_TRACE << "Buffer created with size " << initialSize;
}
~Buffer()
{
LOG_TRACE << "Buffer destroyed, had " << readableBytes() << " readable bytes";
}
size_t readableBytes() const { return writeIndex_ - readIndex_; }
size_t writableBytes() const { return buffer_.size() - writeIndex_; }
size_t prependableBytes() const { return readIndex_; }
const char* peek() const { return &buffer_[readIndex_]; }
char* beginWrite() { return &buffer_[writeIndex_]; }
const char* beginWrite() const { return &buffer_[writeIndex_]; }
void retrieve(size_t len)
{
if (len < readableBytes()) {
readIndex_ += len;
} else {
retrieveAll();
}
}
void retrieveAll()
{
if (buffer_.size() > (initialCap_ * 2)) {
buffer_.resize(initialCap_ + kBufferOffset);
LOG_DEBUG << "Buffer shrunk to " << buffer_.size();
}
readIndex_ = kBufferOffset;
writeIndex_ = kBufferOffset;
}
void hasWritten(size_t len)
{
writeIndex_ += len;
LOG_TRACE << "Buffer written " << len << " bytes, total readable: " << readableBytes();
}
void append(const char* data, size_t len)
{
ensureWritableBytes(len);
std::copy(data, data + len, beginWrite());
hasWritten(len);
}
void append(const std::string& str)
{
append(str.data(), str.size());
}
void ensureWritableBytes(size_t len)
{
if (writableBytes() >= len) return;
if (readIndex_ + writableBytes() >= (len + kBufferOffset)) {
// Move readable data to front
std::copy(&buffer_[readIndex_], &buffer_[writeIndex_], &buffer_[kBufferOffset]);
writeIndex_ = kBufferOffset + (writeIndex_ - readIndex_);
readIndex_ = kBufferOffset;
LOG_TRACE << "Buffer compacted, readable bytes: " << readableBytes();
} else {
// Grow buffer
size_t newLen = std::max(buffer_.size() * 2, kBufferOffset + readableBytes() + len);
buffer_.resize(newLen);
LOG_DEBUG << "Buffer grown to " << newLen;
}
}
// Network byte order append functions
void appendInt8(uint8_t x)
{
append(reinterpret_cast<const char*>(&x), sizeof(x));
}
void appendInt16(uint16_t x)
{
uint16_t be = htons(x);
append(reinterpret_cast<const char*>(&be), sizeof(be));
}
void appendInt32(uint32_t x)
{
uint32_t be = htonl(x);
append(reinterpret_cast<const char*>(&be), sizeof(be));
}
void appendInt64(uint64_t x)
{
uint64_t be = hton64(x);
append(reinterpret_cast<const char*>(&be), sizeof(be));
}
// Network byte order read functions
uint8_t readInt8()
{
uint8_t result = peekInt8();
retrieve(sizeof(result));
return result;
}
uint16_t readInt16()
{
uint16_t result = peekInt16();
retrieve(sizeof(result));
return result;
}
uint32_t readInt32()
{
uint32_t result = peekInt32();
retrieve(sizeof(result));
return result;
}
uint64_t readInt64()
{
uint64_t result = peekInt64();
retrieve(sizeof(result));
return result;
}
// Network byte order peek functions
uint8_t peekInt8() const
{
assert(readableBytes() >= sizeof(uint8_t));
return *reinterpret_cast<const uint8_t*>(peek());
}
uint16_t peekInt16() const
{
assert(readableBytes() >= sizeof(uint16_t));
uint16_t be = *reinterpret_cast<const uint16_t*>(peek());
return ntohs(be);
}
uint32_t peekInt32() const
{
assert(readableBytes() >= sizeof(uint32_t));
uint32_t be = *reinterpret_cast<const uint32_t*>(peek());
return ntohl(be);
}
uint64_t peekInt64() const
{
assert(readableBytes() >= sizeof(uint64_t));
uint64_t be = *reinterpret_cast<const uint64_t*>(peek());
return ntoh64(be);
}
// Prepend functions for efficient header insertion
void prepend(const char* data, size_t len)
{
assert(len <= prependableBytes());
readIndex_ -= len;
std::copy(data, data + len, &buffer_[readIndex_]);
}
void prependInt8(uint8_t x)
{
prepend(reinterpret_cast<const char*>(&x), sizeof(x));
}
void prependInt16(uint16_t x)
{
uint16_t be = htons(x);
prepend(reinterpret_cast<const char*>(&be), sizeof(be));
}
void prependInt32(uint32_t x)
{
uint32_t be = htonl(x);
prepend(reinterpret_cast<const char*>(&be), sizeof(be));
}
void prependInt64(uint64_t x)
{
uint64_t be = hton64(x);
prepend(reinterpret_cast<const char*>(&be), sizeof(be));
}
std::string read(size_t len)
{
if (len > readableBytes()) len = readableBytes();
std::string result(peek(), len);
retrieve(len);
return result;
}
std::string readAll()
{
return read(readableBytes());
}
ssize_t readFd(int fd, int* savedErrno = nullptr)
{
char extrabuf[65536];
iovec vec[2];
size_t writable = writableBytes();
vec[0].iov_base = beginWrite();
vec[0].iov_len = writable;
vec[1].iov_base = extrabuf;
vec[1].iov_len = sizeof(extrabuf);
const int iovcnt = (writable < sizeof(extrabuf)) ? 2 : 1;
ssize_t n = readv(fd, vec, iovcnt);
if (n < 0) {
if (savedErrno) *savedErrno = errno;
LOG_ERROR << "readFd error: " << strerror(errno);
} else if (static_cast<size_t>(n) <= writable) {
writeIndex_ += n;
} else {
writeIndex_ = buffer_.size();
append(extrabuf, n - writable);
}
LOG_TRACE << "readFd returned " << n << " bytes, buffer now has " << readableBytes();
return n;
}
void swap(Buffer& other) noexcept
{
buffer_.swap(other.buffer_);
std::swap(readIndex_, other.readIndex_);
std::swap(writeIndex_, other.writeIndex_);
std::swap(initialCap_, other.initialCap_);
}
};
} // namespace reactor