1
0
Fork 0
Univerxel/src/core/memory.hpp

295 lines
7.6 KiB
C++

#pragma once
#include <vector>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <memory>
/// Bytes manipulation
namespace memory {
/// Abstract data with moving cursor
struct abstract_view {
public:
abstract_view(size_t size): siz(size) { }
constexpr size_t size() const { return siz; }
constexpr bool isDone() const { return cur >= siz; }
protected:
size_t siz;
size_t cur = 0;
};
// Abstract data writer
struct write_view: abstract_view {
public:
write_view(uint8_t *ptr, size_t size): abstract_view(size), ptr(ptr) {
assert(ptr != nullptr);
}
uint8_t* writeTo(size_t len) {
assert(cur + len <= siz);
auto p = ptr + cur;
cur += len;
return p;
}
void write(const uint8_t* data, size_t len) {
memcpy(writeTo(len), data, len);
}
template<typename D>
void write(const D& d) {
write((const uint8_t*)&d, sizeof(d));
}
constexpr size_t current() const { return cur; }
protected:
uint8_t* ptr;
};
struct read_buffer;
/// Writer with Dynamic allocation and subparts
struct write_buffer: public write_view {
private:
static constexpr auto EMPTY_OFFSET = 0x1000;
static uint8_t* alloc(size_t size) { return (uint8_t*)(size > 0 ? ::malloc(size) : reinterpret_cast<uint8_t*>(EMPTY_OFFSET)); }
static uint8_t* realloc(uint8_t* ptr, size_t size) { return (uint8_t*)::realloc(ptr, size); }
static void free(uint8_t* ptr) { ::free(ptr); }
inline void auto_size(size_t len) {
const auto target = cur + len;
if (target > vis_siz) {
reserve(target * 1.5);
vis_siz = target;
}
}
public:
write_buffer(size_t init_size = 0): write_view(alloc(init_size), init_size), vis_siz(init_size) { }
template<typename P>
write_buffer(const P& prefix, size_t additional_size): write_buffer(sizeof(prefix) + additional_size) {
write(prefix);
}
~write_buffer() { if (ptr != reinterpret_cast<uint8_t*>(EMPTY_OFFSET)) free(ptr); }
size_t vis_siz = 0;
uint8_t* data() { return ptr; }
constexpr size_t size() const { return vis_siz; }
void write(const void* data, size_t len) {
write_view::write((uint8_t*)data, len);
}
/// Write without resize
template<typename D>
void write(const D& d) {
write(&d, sizeof(d));
}
uint8_t* pushTo(size_t len) {
auto_size(len);
return writeTo(len);
}
void push(const void* data, size_t len) {
auto_size(len);
write_view::write((uint8_t*)data, len);
}
template<typename D>
void push(const D& d) {
push(&d, sizeof(d));
}
void pop(size_t len) {
assert(cur >= len);
cur -= len;
vis_siz = cur;
}
constexpr size_t getCursor() const { return cur; }
void writeAt(size_t pos, const void* data, size_t len) {
assert(pos + len <= vis_siz);
memcpy(ptr + pos, data, len);
}
template<typename D>
void writeAt(size_t pos, const D& d) {
writeAt(pos, &d, sizeof(d));
}
void reserve(size_t target) {
if (target >= siz) {
ptr = realloc(ptr, target);
siz = target;
}
}
void resize(size_t target) {
reserve(target);
vis_siz = target;
}
bool isFull() const {
return cur >= vis_siz;
}
[[nodiscard]]
read_buffer finish();
// Must take ptr ownership before
void reset() {
ptr = reinterpret_cast<uint8_t*>(EMPTY_OFFSET);
cur = 0;
siz = 0;
vis_siz = 0;
}
struct part {
part(write_buffer* parent): up(parent), start(parent->cur) { }
write_buffer* up;
size_t start;
uint8_t* data() { return up->data() + start; }
constexpr size_t size() const { return up->size() - start; }
void reserve(size_t size) {
up->reserve(start + size);
}
void resize(size_t size) {
up->resize(start + size);
}
};
template<typename CB>
size_t pushPart(const CB& callback) {
const auto prev = cur;
callback(part(this));
cur = vis_siz;
return cur - prev;
}
template<typename CB>
size_t pushSizedPart(const CB& callback) {
const auto size_cur = cur;
push<size_t>(0);
const size_t size = pushPart(callback);
writeAt(size_cur, size);
return size;
}
};
/// Vector with write_view interface
struct write_vector {
/// 512 Mébibits
static constexpr size_t MAX_SIZE = 1ul << 26;
write_vector(size_t max_size = MAX_SIZE): max_size(max_size) {}
std::vector<uint8_t> data;
size_t max_size;
uint8_t* writeTo(size_t len) {
const auto offset = data.size();
assert(offset + len <= max_size);
data.resize(offset + len);
return data.data() + offset;
}
void write(const uint8_t* data, size_t len) {
memcpy(writeTo(len), data, len);
}
template<typename D>
void write(const D& d) {
write(&d, sizeof(d));
}
};
/// Abstract data reader
struct read_view: abstract_view {
read_view(): read_view(nullptr, 0) { }
read_view(const uint8_t *ptr, size_t size): abstract_view(size), ptr(ptr) { }
template<typename D>
static read_view Of(const D& ref) {
return read_view((uint8_t*)&ref, sizeof(ref));
}
const uint8_t* ptr;
const uint8_t* readFrom(size_t len) {
assert(cur + len <= siz);
auto p = ptr + cur;
cur += len;
return p;
}
void read(uint8_t* out, size_t len) {
memcpy(out, readFrom(len), len);
}
constexpr const uint8_t* data() const { return ptr; }
constexpr size_t remaining() const { return size() - cur; }
template<typename D>
const D* read() {
return cur + sizeof(D) <= size() ?
(const D*)readFrom(sizeof(D)) : nullptr;
}
template<typename D>
bool read(D& out) {
if (cur + sizeof(D) > size())
return false;
read((uint8_t*)&out, sizeof(D));
return true;
}
bool skip(size_t size) {
readFrom(size);
return !isDone();
}
read_view readPart(size_t size) {
return read_view(readFrom(size), size);
}
read_view readSizedPart() {
size_t size = 0;
read(size);
return readPart(size);
}
read_view readRemaining() {
return readPart(remaining());
}
};
/// Pointer to opaque owned memory
using handle_t = std::shared_ptr<const void>;
/// read_view with owned data
struct read_buffer: read_view {
read_buffer(): read_view() { }
read_buffer(const read_view& view, const handle_t& handle):
read_view(view), handle(handle) { }
/// Take view ownership
read_buffer(const read_view& view):
read_buffer(view, std::shared_ptr<const void>(view.ptr)) { }
/// Take ptr ownership
read_buffer(const uint8_t *ptr, size_t size): read_buffer(read_view(ptr, size)) { }
/// Take ptr ownership (release writer)
read_buffer(write_buffer& writer):
read_buffer(writer.data(), writer.size()) { writer.reset(); }
handle_t handle = nullptr;
template<typename P>
[[nodiscard]]
static read_buffer Of(const P& prefix, const void* data, size_t size) {
auto writer = write_buffer(prefix, size);
writer.write(data, size);
return writer.finish();
}
template<typename P, typename D>
[[nodiscard]]
static read_buffer Of(const P& prefix, const D& data) {
return Of(prefix, &data, sizeof(data));
}
};
inline read_buffer write_buffer::finish() {
assert(isFull());
return read_buffer(*this);
}
}