nostalgia/deps/ox/src/ox/ptrarith/nodebuffer.hpp

343 lines
8.6 KiB
C++

/*
* Copyright 2015 - 2018 gtalent2@gmail.com
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/trace/trace.hpp>
#include "ptr.hpp"
namespace ox::ptrarith {
template<typename size_t, typename Item>
class __attribute__((packed)) NodeBuffer {
public:
struct __attribute__((packed)) Header {
ox::LittleEndian<size_t> size = sizeof(Header);
ox::LittleEndian<size_t> bytesUsed = sizeof(Header);
ox::LittleEndian<size_t> firstItem = 0;
};
using ItemPtr = Ptr<Item, size_t, sizeof(Header)>;
class Iterator {
private:
NodeBuffer *m_buffer = nullptr;
ItemPtr m_current;
size_t m_it = 0;
public:
Iterator(NodeBuffer *buffer, ItemPtr current) {
m_buffer = buffer;
m_current = current;
oxTrace("ox::ptrarith::Iterator::start") << current.offset();
}
operator const Item*() const {
return m_current;
}
operator Item*() {
return m_current;
}
const Item *operator->() const {
return m_current;
}
Item *operator->() {
return m_current;
}
bool hasNext() {
if (m_current.valid()) {
oxTrace("ox::ptrarith::NodeBuffer::Iterator::hasNext::current") << m_current.offset();
auto next = m_buffer->next(m_current);
return next.valid() && m_buffer->firstItem() != next;
}
return false;
}
void next() {
oxTrace("ox::ptrarith::NodeBuffer::Iterator::next") << m_it++;
m_current = m_buffer->next(m_current);
}
};
Header m_header;
public:
NodeBuffer() = default;
explicit NodeBuffer(size_t size);
const Iterator iterator() const;
Iterator iterator();
ItemPtr firstItem();
ItemPtr lastItem();
/**
* @return the data section of the given item
*/
template<typename T>
Ptr<T, size_t, sizeof(Item)> dataOf(ItemPtr);
ItemPtr prev(Item *item);
ItemPtr next(Item *item);
ItemPtr ptr(size_t offset);
ItemPtr ptr(void *item);
ItemPtr malloc(size_t size);
void free(ItemPtr item);
bool valid(size_t maxSize);
Error setSize(size_t size);
size_t size();
/**
* @return the bytes still available in this NodeBuffer
*/
size_t available();
/**
* @return the actual number a bytes need to store the given number of
* bytes
*/
size_t spaceNeeded(size_t size);
private:
void compact(void (*cb)(ItemPtr itemMoved));
uint8_t *data();
};
template<typename size_t, typename Item>
NodeBuffer<size_t, Item>::NodeBuffer(size_t size) {
m_header.size = size;
}
template<typename size_t, typename Item>
const typename NodeBuffer<size_t, Item>::Iterator NodeBuffer<size_t, Item>::iterator() const {
return Iterator(this, firstItem());
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::Iterator NodeBuffer<size_t, Item>::iterator() {
oxTrace("ox::ptrarith::NodeBuffer::iterator::size") << m_header.size;
return Iterator(this, firstItem());
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::firstItem() {
oxTrace("ox::ptrarith::NodeBuffer::firstItem") << m_header.firstItem;
return ptr(m_header.firstItem);
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::lastItem() {
auto first = ptr(m_header.firstItem);
if (first.valid()) {
return prev(first);
}
return nullptr;
}
template<typename size_t, typename Item>
template<typename T>
Ptr<T, size_t, sizeof(Item)> NodeBuffer<size_t, Item>::dataOf(ItemPtr ip) {
auto out = ip.template subPtr<T>(sizeof(Item));
oxAssert(out.size() == ip.size() - sizeof(Item), "Sub Ptr has invalid size.");
return out;
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::prev(Item *item) {
return ptr(item->prev);
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::next(Item *item) {
return ptr(item->next);
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::ptr(size_t itemOffset) {
// make sure this can be read as an Item, and then use Item::size for the size
auto itemSpace = m_header.size - itemOffset;
auto item = reinterpret_cast<Item*>(reinterpret_cast<uint8_t*>(this) + itemOffset);
oxTrace("ox::ptrarith::NodeBuffer::ptr::itemOffset") << itemOffset << m_header.size - sizeof(Item);
if (itemOffset >= sizeof(Header) and
itemOffset < m_header.size - sizeof(Item) and
itemSpace >= static_cast<size_t>(sizeof(Item)) and
itemSpace >= item->fullSize()) {
return ItemPtr(this, m_header.size, itemOffset, item->fullSize());
} else {
oxTrace("ox::ptrarith::NodeBuffer::ptr::null") << itemOffset;
return ItemPtr(this, m_header.size, 0, 0);
}
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::malloc(size_t size) {
auto fullSize = size + sizeof(Item);
if (m_header.size - m_header.bytesUsed >= fullSize) {
auto last = lastItem();
size_t addr = 0;
if (last.valid()) {
addr = last.offset() + last.size();
} else {
// there is no first item, so this may be the first item
if (!m_header.firstItem) {
oxTrace("ox::fs::NodeBuffer::malloc") << "No first item, initializing.";
m_header.firstItem = sizeof(m_header);
addr = m_header.firstItem;
}
}
auto out = ItemPtr(this, m_header.size, addr, fullSize);
if (out.valid()) {
new (out) Item;
out->setSize(size);
auto first = firstItem();
out->next = first.offset();
if (first.valid()) {
first->prev = out.offset();
} else {
oxTrace("ox::fs::NodeBuffer::malloc::fail") << "NodeBuffer malloc failed due to invalid first element pointer.";
return nullptr;
}
auto last = lastItem();
out->prev = last.offset();
if (last.valid()) {
last->next = out.offset();
} else {
oxTrace("ox::fs::NodeBuffer::malloc::fail") << "NodeBuffer malloc failed due to invalid last element pointer.";
return nullptr;
}
m_header.bytesUsed += out.size();
} else {
oxTrace("ox::fs::NodeBuffer::malloc::fail") << "Unknown";
}
return out;
}
oxTrace("ox::fs::NodeBuffer::malloc::fail") << "Insufficient space:" << fullSize << "needed," << available() << "available";
return nullptr;
}
template<typename size_t, typename Item>
void NodeBuffer<size_t, Item>::free(ItemPtr item) {
auto prev = this->prev(item);
auto next = this->next(item);
if (prev.valid()) {
prev->next = next.offset();
} else {
oxTrace("ox::fs::NodeBuffer::free::fail") << "NodeBuffer free failed due to invalid prev element pointer.";
}
if (next.valid()) {
next->prev = prev.offset();
} else {
oxTrace("ox::fs::NodeBuffer::free::fail") << "NodeBuffer free failed due to invalid next element pointer.";
}
m_header.bytesUsed -= item.size();
}
template<typename size_t, typename Item>
Error NodeBuffer<size_t, Item>::setSize(size_t size) {
auto last = lastItem();
if ((last.valid() and last.end() >= size) or size < sizeof(m_header)) {
return 1;
} else {
m_header.size = size;
return 0;
}
}
template<typename size_t, typename Item>
size_t NodeBuffer<size_t, Item>::size() {
return m_header.size;
}
template<typename size_t, typename Item>
bool NodeBuffer<size_t, Item>::valid(size_t maxSize) {
return m_header.size <= maxSize;
}
template<typename size_t, typename Item>
size_t NodeBuffer<size_t, Item>::available() {
return m_header.size - m_header.bytesUsed;
}
template<typename size_t, typename Item>
size_t NodeBuffer<size_t, Item>::spaceNeeded(size_t size) {
return sizeof(Item) + size;
}
template<typename size_t, typename Item>
void NodeBuffer<size_t, Item>::compact(void (*cb)(ItemPtr)) {
auto src = firstItem();
auto dest = ptr(sizeof(*this));
while (src.valid() && dest.valid()) {
// move node
ox_memcpy(dest, src, src.size());
if (cb) {
cb(dest);
}
// update surrounding nodes
auto prev = ptr(dest->next);
if (prev.valid()) {
prev->next = dest;
}
auto next = ptr(dest->next);
if (next.valid()) {
next->prev = dest;
}
// update iterators
src = ptr(dest->next);
dest = ptr(dest.offset() + dest.size());
}
}
template<typename size_t, typename Item>
uint8_t *NodeBuffer<size_t, Item>::data() {
return reinterpret_cast<uint8_t*>(ptr(sizeof(*this)).get());
}
template<typename size_t>
struct __attribute__((packed)) Item {
public:
ox::LittleEndian<size_t> prev = 0;
ox::LittleEndian<size_t> next = 0;
private:
ox::LittleEndian<size_t> m_size = sizeof(Item);
public:
size_t size() const {
return m_size;
}
void setSize(size_t size) {
m_size = size;
}
};
}