1
0
mirror of https://github.com/danog/libtgvoip.git synced 2024-12-04 10:37:49 +01:00
libtgvoip/tools/Buffers.h

412 lines
8.6 KiB
C
Raw Normal View History

2020-01-22 12:43:51 +01:00
//
// 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.
//
2020-01-23 18:06:11 +01:00
#pragma once
2020-01-22 12:43:51 +01:00
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdexcept>
#include <array>
#include <limits>
2020-01-25 16:13:16 +01:00
#include <algorithm>
#include <numeric>
2020-01-22 12:43:51 +01:00
#include <bitset>
2020-01-25 16:13:16 +01:00
#include <memory>
2020-01-22 12:43:51 +01:00
#include <stddef.h>
2020-01-25 16:04:52 +01:00
#include "threading.h"
#include "utils.h"
2020-01-22 12:43:51 +01:00
namespace tgvoip
{
class Buffer;
class BufferInputStream
{
public:
BufferInputStream(const unsigned char *data, size_t length);
BufferInputStream(const Buffer &buffer);
~BufferInputStream();
void Seek(size_t offset);
2020-01-25 16:13:16 +01:00
size_t GetLength() const;
size_t GetOffset() const;
size_t Remaining() const;
2020-01-22 12:43:51 +01:00
unsigned char ReadByte();
int64_t ReadInt64();
int32_t ReadInt32();
int16_t ReadInt16();
2020-01-25 16:13:16 +01:00
uint32_t ReadTlLength();
2020-01-22 12:43:51 +01:00
void ReadBytes(unsigned char *to, size_t count);
void ReadBytes(Buffer &to);
BufferInputStream GetPartBuffer(size_t length, bool advance);
2020-01-25 16:13:16 +01:00
inline uint64_t ReadUInt64()
{
return static_cast<uint64_t>(ReadInt64());
}
inline uint32_t ReadUInt32()
{
return static_cast<uint32_t>(ReadInt32());
}
inline uint16_t ReadUInt16()
{
return static_cast<uint16_t>(ReadInt16());
}
2020-01-22 12:43:51 +01:00
private:
void EnsureEnoughRemaining(size_t need);
const unsigned char *buffer;
size_t length;
2020-01-25 16:13:16 +01:00
size_t offset = 0;
2020-01-22 12:43:51 +01:00
};
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);
unsigned char *GetBuffer();
size_t GetLength();
void Reset();
void Rewind(size_t numBytes);
2020-01-25 16:13:16 +01:00
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));
}
2020-01-22 12:43:51 +01:00
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;
2020-01-25 16:13:16 +01:00
size_t offset = 0;
2020-01-22 12:43:51 +01:00
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;
}
2020-03-10 19:36:27 +01:00
void CopyFromOtherBuffer(const Buffer &other, size_t count, size_t srcOffset = 0, size_t dstOffset = 0)
2020-01-22 12:43:51 +01:00
{
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);
2020-03-10 19:36:27 +01:00
buf.CopyFromOtherBuffer(other, other.length);
2020-01-22 12:43:51 +01:00
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);
2020-03-10 19:36:27 +01:00
buf.CopyFromOtherBuffer(other, length, offset);
2020-01-22 12:43:51 +01:00
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)
2020-01-22 12:43:51 +01:00
{
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 <typename T, size_t size, typename AVG_T = T>
class HistoricBuffer
{
public:
HistoricBuffer()
{
}
AVG_T Average() const
{
2020-01-25 16:13:16 +01:00
return std::accumulate(data.begin(), data.end(), static_cast<AVG_T>(0)) / static_cast<AVG_T>(size);
2020-01-22 12:43:51 +01:00
}
AVG_T Average(size_t firstN) const
{
2020-01-25 16:13:16 +01:00
AVG_T avg = static_cast<AVG_T>(0);
for (size_t i = 0; i < firstN; i++) // Manual iteration required to wrap around array with specific offset
2020-01-22 12:43:51 +01:00
{
avg += (*this)[i];
}
2020-01-25 16:13:16 +01:00
return avg / static_cast<AVG_T>(firstN);
2020-01-22 12:43:51 +01:00
}
AVG_T NonZeroAverage() const
{
2020-01-25 16:13:16 +01:00
AVG_T avg = static_cast<AVG_T>(0);
2020-01-22 12:43:51 +01:00
int nonZeroCount = 0;
for (T i : data)
{
if (i != 0)
{
nonZeroCount++;
avg += i;
}
}
if (nonZeroCount == 0)
2020-01-25 16:13:16 +01:00
return static_cast<AVG_T>(0);
return avg / static_cast<AVG_T>(nonZeroCount);
2020-01-22 12:43:51 +01:00
}
void Add(T el)
{
data[offset] = el;
offset = (offset + 1) % size;
}
T Min() const
{
2020-01-25 16:13:16 +01:00
return *std::min_element(data.begin(), data.end());
2020-01-22 12:43:51 +01:00
}
T Max() const
{
2020-01-25 16:13:16 +01:00
return *std::max_element(data.begin(), data.end());
2020-01-22 12:43:51 +01:00
}
void Reset()
{
2020-01-25 16:13:16 +01:00
std::fill(data.begin(), data.end(), static_cast<T>(0));
2020-01-22 12:43:51 +01:00
offset = 0;
}
T operator[](size_t i) const
{
assert(i < size);
// [0] should return the most recent entry, [1] the one before it, and so on
ptrdiff_t _i = offset - i - 1;
2020-01-25 16:13:16 +01:00
if (_i < 0) // Wrap around offset a-la posmod
_i = size + _i;
2020-01-22 12:43:51 +01:00
return data[_i];
}
T &operator[](size_t i)
{
assert(i < size);
// [0] should return the most recent entry, [1] the one before it, and so on
ptrdiff_t _i = offset - i - 1;
2020-01-25 16:13:16 +01:00
if (_i < 0) // Wrap around offset a-la posmod
_i = size + _i;
2020-01-22 12:43:51 +01:00
return data[_i];
}
2020-01-27 16:02:59 +01:00
constexpr size_t Size() const
2020-01-22 12:43:51 +01:00
{
return size;
}
private:
2020-01-25 16:13:16 +01:00
std::array<T, size> data{};
2020-01-22 12:43:51 +01:00
ptrdiff_t offset = 0;
};
template <size_t bufSize, size_t bufCount>
class BufferPool
{
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(BufferPool);
2020-01-25 16:04:52 +01:00
BufferPool() : bufferStart(new unsigned char[bufSize * bufCount], std::default_delete<unsigned char[]>()) {}
~BufferPool(){};
2020-01-22 12:43:51 +01:00
Buffer Get()
{
static auto resizeFn = [](void *buf, size_t newSize) -> void * {
2020-01-22 12:43:51 +01:00
if (newSize > bufSize)
throw std::invalid_argument("newSize>bufferSize");
return buf;
};
MutexGuard m(mutex);
for (size_t i = 0; i < bufCount; i++)
{
2020-01-25 16:04:52 +01:00
if (!usedBuffers[offset])
2020-01-22 12:43:51 +01:00
{
2020-01-25 16:04:52 +01:00
size_t offsetCopy = offset;
offset = (offset + 1) % bufCount;
usedBuffers[offsetCopy] = 1;
auto freeFn = [this, offsetCopy, lock = bufferStart](void *_buf) mutable {
2020-01-25 16:04:52 +01:00
MutexGuard m(mutex);
usedBuffers[offsetCopy] = 0;
lock.reset();
};
return Buffer::Wrap(bufferStart.get() + (bufSize * offsetCopy), bufSize, freeFn, resizeFn);
2020-01-22 12:43:51 +01:00
}
}
throw std::bad_alloc();
}
private:
std::bitset<bufCount> usedBuffers;
2020-01-25 16:04:52 +01:00
size_t offset = 0;
std::shared_ptr<unsigned char> bufferStart;
2020-01-22 12:43:51 +01:00
Mutex mutex;
};
} // namespace tgvoip