126 lines
2.7 KiB
C++
126 lines
2.7 KiB
C++
#pragma once
|
|
|
|
#include <cstring>
|
|
#include <uv.h>
|
|
|
|
#include "utils/memory/block_allocator.hpp"
|
|
|
|
namespace uv
|
|
{
|
|
|
|
template <size_t block_size>
|
|
class BlockBuffer
|
|
{
|
|
static BlockAllocator<block_size> allocator;
|
|
|
|
struct Block : public uv_buf_t
|
|
{
|
|
Block()
|
|
{
|
|
// acquire a new block of memory for this buffer
|
|
base = static_cast<char*>(allocator.acquire());
|
|
len = 0;
|
|
}
|
|
|
|
~Block()
|
|
{
|
|
// release the block of memory previously acquired
|
|
allocator.release(base);
|
|
}
|
|
|
|
size_t append(const char* data, size_t size)
|
|
{
|
|
// compute the remaining capacity for this block
|
|
auto capacity = block_size - len;
|
|
|
|
// if the capacity is smaller than the requested size, copy only
|
|
// up to the remaining capacity
|
|
if(size > capacity)
|
|
size = capacity;
|
|
|
|
std::memcpy(base + len, data, size);
|
|
len += size;
|
|
|
|
// return how much we've copied
|
|
return size;
|
|
}
|
|
};
|
|
|
|
public:
|
|
BlockBuffer()
|
|
{
|
|
// create the first buffer
|
|
buffers.emplace_back();
|
|
}
|
|
|
|
~BlockBuffer()
|
|
{
|
|
buffers.clear();
|
|
}
|
|
|
|
BlockBuffer(BlockBuffer&) = delete;
|
|
BlockBuffer(BlockBuffer&&) = delete;
|
|
|
|
size_t count() const
|
|
{
|
|
return buffers.size();
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
// pop all buffers except for the first one since we need to allocate
|
|
// it again anyway so why not keep it in the first place
|
|
while(count() > 1)
|
|
buffers.pop_back();
|
|
|
|
// pretend we just allocated our first buffer and set it's length to 0
|
|
buffers.back().len = 0;
|
|
}
|
|
|
|
BlockBuffer& operator<<(const std::string& data)
|
|
{
|
|
append(data);
|
|
return *this;
|
|
}
|
|
|
|
void append(const std::string& data)
|
|
{
|
|
append(data.c_str(), data.size());
|
|
}
|
|
|
|
void append(const char* data, size_t size)
|
|
{
|
|
while(true)
|
|
{
|
|
// try to copy as much data as possible
|
|
auto copied = buffers.back().append(data, size);
|
|
|
|
// if we managed to copy everything, we're done
|
|
if(copied == size)
|
|
break;
|
|
|
|
// move the pointer past the copied part
|
|
data += copied;
|
|
|
|
// reduce the total size by the number of copied items
|
|
size -= copied;
|
|
|
|
// since we ran out of space, construct a new buffer
|
|
buffers.emplace_back();
|
|
}
|
|
}
|
|
|
|
operator uv_buf_t*()
|
|
{
|
|
return buffers.data();
|
|
}
|
|
|
|
private:
|
|
std::vector<Block> buffers;
|
|
};
|
|
|
|
template <size_t block_size>
|
|
BlockAllocator<block_size> BlockBuffer<block_size>::allocator;
|
|
|
|
}
|