1
0
mirror of https://github.com/danog/libtgvoip.git synced 2024-12-02 09:37:52 +01:00
libtgvoip/tools/Buffers.h
2020-03-29 17:20:17 +02:00

358 lines
9.5 KiB
C++

//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#pragma once
#include "threading.h"
#include "utils.h"
#include <algorithm>
#include <array>
#include <bitset>
#include <limits>
#include <memory>
#include <numeric>
#include <stddef.h>
#include <stdexcept>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
namespace tgvoip
{
class Buffer;
class NetworkAddress;
struct Serializable;
struct VersionInfo;
class BufferInputStream
{
public:
BufferInputStream(const unsigned char *data, size_t length);
BufferInputStream(const Buffer &buffer);
BufferInputStream() = default;
~BufferInputStream() = default;
void Seek(size_t offset) const;
size_t GetLength() const;
size_t GetOffset() const;
size_t Remaining() const;
unsigned char ReadByte() const;
int64_t ReadInt64() const;
int32_t ReadInt32() const;
int16_t ReadInt16() const;
uint32_t ReadTlLength() const;
void ReadBytes(unsigned char *to, size_t count) const;
void ReadBytes(Buffer &to) const;
BufferInputStream GetPartBuffer(size_t length, bool advance = true) const;
const unsigned char *GetRawBuffer() const;
inline uint64_t ReadUInt64() const
{
return static_cast<uint64_t>(ReadInt64());
}
inline uint32_t ReadUInt32() const
{
return static_cast<uint32_t>(ReadInt32());
}
inline uint16_t ReadUInt16() const
{
return static_cast<uint16_t>(ReadInt16());
}
bool TryRead(uint8_t &data) const;
bool TryRead(uint16_t &data) const;
bool TryRead(uint32_t &data) const;
bool TryRead(uint64_t &data) const;
bool TryRead(int16_t &data) const;
bool TryRead(int32_t &data) const;
bool TryRead(int64_t &data) const;
bool TryRead(uint8_t *to, size_t count) const;
bool TryRead(Buffer &to) const;
bool TryRead(NetworkAddress &to, bool ipv6) const;
bool TryRead(Serializable &to, const VersionInfo &ver) const;
template <typename X, typename Y>
bool TryReadCompat(Y &data) const
// For compatibility, actually read type X, then cast to Y
{
X temp;
if (!TryRead(temp))
return false;
data = static_cast<Y>(temp);
return true;
}
bool Has(size_t length) const;
bool TryReadTlLength(uint32_t &data) const;
private:
void EnsureEnoughRemaining(size_t need) const;
const unsigned char *buffer = nullptr;
size_t length = 0;
mutable size_t offset = 0;
}; // namespace tgvoip
class BufferOutputStream
{
friend class Buffer;
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(BufferOutputStream);
BufferOutputStream(size_t size);
BufferOutputStream(unsigned char *buffer, size_t size);
~BufferOutputStream();
void WriteByte(unsigned char byte);
void WriteInt64(int64_t i);
void WriteInt32(int32_t i);
void WriteInt16(int16_t i);
void WriteBytes(const unsigned char *bytes, size_t count);
void WriteBytes(const Buffer &buffer);
void WriteBytes(const Buffer &buffer, size_t offset, size_t count);
void Write(const Serializable &data, const VersionInfo &ver);
unsigned char *GetBuffer();
size_t GetOffset();
size_t GetLength();
void Reset();
void Rewind(size_t numBytes);
void Advance(size_t numBytes);
inline void WriteUInt64(uint64_t i)
{
WriteInt64(static_cast<int64_t>(i));
}
inline void WriteUInt32(uint32_t i)
{
WriteInt32(static_cast<int32_t>(i));
}
inline void WriteUInt16(uint16_t i)
{
WriteInt16(static_cast<int16_t>(i));
}
BufferOutputStream &operator=(BufferOutputStream &&other)
{
if (this != &other)
{
if (!bufferProvided && buffer)
free(buffer);
buffer = other.buffer;
offset = other.offset;
size = other.size;
bufferProvided = other.bufferProvided;
other.buffer = NULL;
}
return *this;
}
private:
void ExpandBufferIfNeeded(size_t need);
unsigned char *buffer = NULL;
size_t size;
size_t offset = 0;
bool bufferProvided;
};
class Buffer
{
public:
Buffer(size_t capacity)
{
if (capacity > 0)
{
data = (unsigned char *)malloc(capacity);
if (!data)
throw std::bad_alloc();
}
else
{
data = NULL;
}
length = capacity;
};
TGVOIP_DISALLOW_COPY_AND_ASSIGN(Buffer); // use Buffer::CopyOf to copy contents explicitly
Buffer(Buffer &&other) noexcept
{
data = other.data;
length = other.length;
freeFn = other.freeFn;
reallocFn = other.reallocFn;
other.data = NULL;
};
Buffer(BufferOutputStream &&stream)
{
data = stream.buffer;
length = stream.offset;
stream.buffer = NULL;
}
Buffer()
{
data = NULL;
length = 0;
}
~Buffer()
{
if (data)
{
if (freeFn)
freeFn(data);
else
free(data);
}
data = NULL;
length = 0;
};
Buffer &operator=(Buffer &&other)
{
if (this != &other)
{
if (data)
{
if (freeFn)
freeFn(data);
else
free(data);
}
data = other.data;
length = other.length;
freeFn = other.freeFn;
reallocFn = other.reallocFn;
other.data = NULL;
other.length = 0;
}
return *this;
}
unsigned char &operator[](size_t i)
{
if (i >= length)
throw std::out_of_range("");
return data[i];
}
const unsigned char &operator[](size_t i) const
{
if (i >= length)
throw std::out_of_range("");
return data[i];
}
unsigned char *operator*()
{
return data;
}
const unsigned char *operator*() const
{
return data;
}
operator bool() const
{
return length;
}
void CopyFromOtherBuffer(const Buffer &other, size_t count, size_t srcOffset = 0, size_t dstOffset = 0)
{
if (!other.data)
throw std::invalid_argument("CopyFrom can't copy from NULL");
if (other.length < srcOffset + count || length < dstOffset + count)
throw std::out_of_range("Out of offset+count bounds of either buffer");
memcpy(data + dstOffset, other.data + srcOffset, count);
}
void CopyFrom(const void *ptr, size_t dstOffset, size_t count)
{
if (length < dstOffset + count)
throw std::out_of_range("Offset+count is out of bounds");
memcpy(data + dstOffset, ptr, count);
}
void Resize(size_t newSize)
{
if (reallocFn)
data = (unsigned char *)reallocFn(data, newSize);
else
data = (unsigned char *)realloc(data, newSize);
if (!data)
throw std::bad_alloc();
length = newSize;
}
size_t Length() const
{
return length;
}
bool IsEmpty() const
{
return length == 0 || !data;
}
static Buffer CopyOf(const Buffer &other)
{
if (other.IsEmpty())
return Buffer();
Buffer buf(other.length);
buf.CopyFromOtherBuffer(other, other.length);
return buf;
}
static Buffer CopyOf(const Buffer &other, size_t offset, size_t length)
{
if (offset + length > other.Length())
throw std::out_of_range("offset+length out of bounds");
Buffer buf(length);
buf.CopyFromOtherBuffer(other, length, offset);
return buf;
}
static Buffer Wrap(unsigned char *data, size_t size, const std::function<void(void *)> &freeFn, const std::function<void *(void *, size_t)> &reallocFn)
{
Buffer b = Buffer();
b.data = data;
b.length = size;
b.freeFn = freeFn;
b.reallocFn = reallocFn;
return b;
}
private:
unsigned char *data;
size_t length;
std::function<void(void *)> freeFn;
std::function<void *(void *, size_t)> reallocFn;
};
template <size_t bufSize, size_t bufCount>
class BufferPool
{
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(BufferPool);
BufferPool() : bufferStart(new unsigned char[bufSize * bufCount], std::default_delete<unsigned char[]>()) {}
~BufferPool(){};
Buffer Get()
{
static auto resizeFn = [](void *buf, size_t newSize) -> void * {
if (newSize > bufSize)
throw std::invalid_argument("newSize>bufferSize");
return buf;
};
MutexGuard m(mutex);
for (size_t i = 0; i < bufCount; i++)
{
if (!usedBuffers[offset])
{
size_t offsetCopy = offset;
offset = (offset + 1) % bufCount;
usedBuffers[offsetCopy] = 1;
auto freeFn = [this, offsetCopy, lock = bufferStart](void *_buf) mutable {
MutexGuard m(mutex);
usedBuffers[offsetCopy] = 0;
lock.reset();
};
return Buffer::Wrap(bufferStart.get() + (bufSize * offsetCopy), bufSize, freeFn, resizeFn);
}
}
throw std::bad_alloc();
}
private:
std::bitset<bufCount> usedBuffers;
size_t offset = 0;
std::shared_ptr<unsigned char> bufferStart;
Mutex mutex;
};
} // namespace tgvoip