summaryrefslogtreecommitdiff
path: root/modules/core
diff options
context:
space:
mode:
authorClaudius "keldu" Holeksa <mail@keldu.de>2023-12-04 12:18:14 +0100
committerClaudius "keldu" Holeksa <mail@keldu.de>2023-12-04 12:18:14 +0100
commita14896f9ed209dd3f9597722e5a5697bd7dbf531 (patch)
tree089ca5cbbd206d1921f8f6b53292f5bc1902ca5c /modules/core
parent84ecdcbca9e55b1f57fbb832e12ff4fdbb86e7c9 (diff)
meta: Renamed folder containing source
Diffstat (limited to 'modules/core')
-rw-r--r--modules/core/.nix/derivation.nix21
-rw-r--r--modules/core/SConscript42
-rw-r--r--modules/core/SConstruct66
-rw-r--r--modules/core/array.h96
-rw-r--r--modules/core/buffer.cpp436
-rw-r--r--modules/core/buffer.h199
-rw-r--r--modules/core/common.h75
-rw-r--r--modules/core/error.cpp156
-rw-r--r--modules/core/error.h248
-rw-r--r--modules/core/id.h54
-rw-r--r--modules/core/id_map.h137
-rw-r--r--modules/core/mcts.h52
-rw-r--r--modules/core/platonic.h103
-rw-r--r--modules/core/string_literal.h57
-rw-r--r--modules/core/templates.h150
-rw-r--r--modules/core/tree.h248
16 files changed, 2140 insertions, 0 deletions
diff --git a/modules/core/.nix/derivation.nix b/modules/core/.nix/derivation.nix
new file mode 100644
index 0000000..1618651
--- /dev/null
+++ b/modules/core/.nix/derivation.nix
@@ -0,0 +1,21 @@
+{ lib
+, stdenv
+, scons
+, clang-tools
+, version
+}:
+
+stdenv.mkDerivation {
+ pname = "forstio-core";
+ inherit version;
+ src = ./..;
+
+ enableParallelBuilding = true;
+
+ nativeBuildInputs = [
+ scons
+ clang-tools
+ ];
+
+ outputs = ["out" "dev"];
+}
diff --git a/modules/core/SConscript b/modules/core/SConscript
new file mode 100644
index 0000000..02bd050
--- /dev/null
+++ b/modules/core/SConscript
@@ -0,0 +1,42 @@
+#!/bin/false
+
+import os
+import os.path
+import glob
+
+
+Import('env')
+
+dir_path = Dir('.').abspath
+
+# Environment for base library
+core_env = env.Clone();
+
+core_env.sources = sorted(glob.glob(dir_path + "/*.cpp"))
+core_env.headers = sorted(glob.glob(dir_path + "/*.h"))
+
+env.sources += core_env.sources;
+env.headers += core_env.headers;
+
+## Shared lib
+objects_shared = []
+core_env.add_source_files(objects_shared, core_env.sources, shared=True);
+core_env.library_shared = core_env.SharedLibrary('#build/forstio-core', [objects_shared]);
+
+## Static lib
+objects_static = []
+core_env.add_source_files(objects_static, core_env.sources, shared=False);
+core_env.library_static = core_env.StaticLibrary('#build/forstio-core', [objects_static]);
+
+# Set Alias
+env.Alias('library_core', [core_env.library_shared, core_env.library_static]);
+
+env.targets += ['library_core'];
+
+# Install
+env.Install('$prefix/lib/', [core_env.library_shared, core_env.library_static]);
+env.Install('$prefix/include/forstio/core/', [core_env.headers]);
+
+# Test
+# Export('core_env');
+# SConscript('tests/SConscript');
diff --git a/modules/core/SConstruct b/modules/core/SConstruct
new file mode 100644
index 0000000..865d131
--- /dev/null
+++ b/modules/core/SConstruct
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+
+import sys
+import os
+import os.path
+import glob
+import re
+
+
+if sys.version_info < (3,):
+ def isbasestring(s):
+ return isinstance(s,basestring)
+else:
+ def isbasestring(s):
+ return isinstance(s, (str,bytes))
+
+def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""):
+
+ if isbasestring(filetype):
+ dir_path = self.Dir('.').abspath
+ filetype = sorted(glob.glob(dir_path+"/"+filetype))
+
+ for path in filetype:
+ target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path )
+ if shared:
+ target_name+='.os'
+ sources.append( self.SharedObject( target=target_name, source=path ) )
+ else:
+ target_name+='.o'
+ sources.append( self.StaticObject( target=target_name, source=path ) )
+ pass
+
+def isAbsolutePath(key, dirname, env):
+ assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,)
+
+env_vars = Variables(
+ args=ARGUMENTS
+)
+
+env_vars.Add('prefix',
+ help='Installation target location of build results and headers',
+ default='/usr/local/',
+ validator=isAbsolutePath
+)
+
+env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[],
+ CPPDEFINES=['SAW_UNIX'],
+ CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'],
+ LIBS=[])
+env.__class__.add_source_files = add_kel_source_files
+env.Tool('compilation_db');
+env.cdb = env.CompilationDatabase('compile_commands.json');
+
+env.objects = [];
+env.sources = [];
+env.headers = [];
+env.targets = [];
+
+Export('env')
+SConscript('SConscript')
+
+env.Alias('cdb', env.cdb);
+env.Alias('all', [env.targets]);
+env.Default('all');
+
+env.Alias('install', '$prefix')
diff --git a/modules/core/array.h b/modules/core/array.h
new file mode 100644
index 0000000..f82b8d6
--- /dev/null
+++ b/modules/core/array.h
@@ -0,0 +1,96 @@
+#pragma once
+
+#include <vector>
+
+namespace saw {
+/**
+ * Array container avoiding exceptions
+ */
+template<typename T>
+class array {
+private:
+ std::vector<T> data_;
+public:
+ array() = default;
+
+ SAW_FORBID_COPY(array);
+
+ T& at(size_t i) noexcept {
+ return data_.at(i);
+ }
+
+ const T& at(size_t i) noexcept const {
+ return data_.at(i);
+ }
+
+ size_t size() noexcept const {
+ return data_.size();
+ }
+
+ T& front() noexcept {
+ return data_.front();
+ }
+
+ const T& front() noexcept const {
+ return data_.front();
+ }
+
+ T& back() noexcept {
+ return data_.back();
+ }
+
+ const T& back() noexcept const {
+ return data_.back();
+ }
+
+ error_or<void> push(T val) noexcept {
+ try{
+ data_.push_back(std::move(val));
+ }catch(std::exception& e){
+ return make_error<err::out_of_memory>();
+ }
+
+ return void_t{};
+ }
+
+ error_or<void> pop() noexcept {
+ try{
+ data_.pop_back();
+ }catch(std::exception& e){
+ return make_error<err::out_of_memory>();
+ }
+
+ return void_t{};
+ }
+
+ error_or<void> resize(size_t i) noexcept {
+ try{
+ data_.resize(i);
+ }catch(std::exception& e){
+ return make_error<err::out_of_memory>();
+ }
+
+ return void_t{};
+ }
+
+ error_or<void> reserve(size_t i) noexcept {
+ try{
+ data_.reserve(i);
+ }catch(std::exception& e){
+ return make_error<err::out_of_memory>();
+ }
+
+ return void_t{};
+ }
+
+ error_or<void> shrink_to_fit() noexcept {
+ try{
+ data_.shrink_to_fit();
+ }catch(std::exception& e){
+ return make_error<err::out_of_memory>();
+ }
+
+ return void_t{};
+ }
+};
+}
diff --git a/modules/core/buffer.cpp b/modules/core/buffer.cpp
new file mode 100644
index 0000000..15f4cae
--- /dev/null
+++ b/modules/core/buffer.cpp
@@ -0,0 +1,436 @@
+#include "buffer.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <iomanip>
+#include <sstream>
+
+namespace saw {
+error buffer::push(const uint8_t &value) {
+ size_t write_remain = write_composite_length();
+ if (write_remain > 0) {
+ write() = value;
+ write_advance(1);
+ } else {
+ return make_error<err::buffer_exhausted>();
+ }
+ return no_error();
+}
+
+error buffer::push(const uint8_t &buffer, size_t size) {
+ error error = write_require_length(size);
+ if (error.failed()) {
+ return error;
+ }
+ const uint8_t *buffer_ptr = &buffer;
+ while (size > 0) {
+ size_t segment = std::min(write_segment_length(), size);
+ memcpy(&write(), buffer_ptr, segment);
+ write_advance(segment);
+ size -= segment;
+ buffer_ptr += segment;
+ }
+ return no_error();
+}
+
+error buffer::pop(uint8_t &value) {
+ if (read_composite_length() > 0) {
+ value = read();
+ read_advance(1);
+ } else {
+ return make_error<err::buffer_exhausted>();
+ }
+ return no_error();
+}
+
+error buffer::pop(uint8_t &buffer, size_t size) {
+ if (read_composite_length() >= size) {
+ uint8_t *buffer_ptr = &buffer;
+ while (size > 0) {
+ size_t segment = std::min(read_segment_length(), size);
+ memcpy(buffer_ptr, &read(), segment);
+ read_advance(segment);
+ size -= segment;
+ buffer_ptr += segment;
+ }
+ } else {
+ return make_error<err::buffer_exhausted>();
+ }
+ return no_error();
+}
+
+/*
+std::string buffer::to_hex() const {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ for (size_t i = 0; i < read_composite_length(); ++i) {
+ oss << std::setw(2) << (uint16_t)read(i);
+ if ((i + 1) < read_composite_length()) {
+ oss << ((i % 4 == 3) ? '\n' : ' ');
+ }
+ }
+ return oss.str();
+}
+*/
+
+std::string convert_to_string(const buffer& buff){
+ std::ostringstream oss;
+ for (size_t i = 0; i < buff.read_composite_length(); ++i) {
+ oss << buff.read(i);
+ }
+ return oss.str();
+}
+
+buffer_view::buffer_view(buffer &buffer)
+ : buffer_{&buffer}, read_offset_{0}, write_offset_{0} {}
+
+size_t buffer_view::read_position() const {
+ return read_offset_ + buffer_->read_position();
+}
+
+size_t buffer_view::read_composite_length() const {
+ assert(read_offset_ <= buffer_->read_composite_length());
+ if (read_offset_ > buffer_->read_composite_length()) {
+ return 0;
+ }
+
+ return buffer_->read_composite_length() - read_offset_;
+}
+
+size_t buffer_view::read_segment_length(size_t offset) const {
+ size_t off = offset + read_offset_;
+ assert(off <= buffer_->read_composite_length());
+ if (off > buffer_->read_composite_length()) {
+ return 0;
+ }
+
+ return buffer_->read_segment_length(off);
+}
+
+void buffer_view::read_advance(size_t bytes) {
+ size_t offset = bytes + read_offset_;
+ assert(offset <= buffer_->read_composite_length());
+ if (offset > buffer_->read_composite_length()) {
+ read_offset_ += buffer_->read_composite_length();
+ return;
+ }
+
+ read_offset_ += bytes;
+}
+
+uint8_t &buffer_view::read(size_t i) {
+ size_t pos = i + read_offset_;
+
+ assert(pos < buffer_->read_composite_length());
+
+ return buffer_->read(pos);
+}
+
+const uint8_t &buffer_view::read(size_t i) const {
+ size_t pos = i + read_offset_;
+
+ assert(pos < buffer_->read_composite_length());
+
+ return buffer_->read(pos);
+}
+
+size_t buffer_view::write_position() const {
+ return write_offset_ + buffer_->write_position();
+}
+
+size_t buffer_view::write_composite_length() const {
+ assert(write_offset_ <= buffer_->write_composite_length());
+ if (write_offset_ > buffer_->write_composite_length()) {
+ return 0;
+ }
+
+ return buffer_->write_composite_length() - write_offset_;
+}
+
+size_t buffer_view::write_segment_length(size_t offset) const {
+ size_t off = offset + write_offset_;
+ assert(off <= buffer_->write_composite_length());
+ if (off > buffer_->write_composite_length()) {
+ return 0;
+ }
+
+ return buffer_->write_segment_length(off);
+}
+
+void buffer_view::write_advance(size_t bytes) {
+ size_t offset = bytes + write_offset_;
+ assert(offset <= buffer_->write_composite_length());
+ if (offset > buffer_->write_composite_length()) {
+ write_offset_ += buffer_->write_composite_length();
+ return;
+ }
+
+ write_offset_ += bytes;
+}
+
+uint8_t &buffer_view::write(size_t i) {
+ size_t pos = i + write_offset_;
+
+ assert(pos < buffer_->write_composite_length());
+
+ return buffer_->write(pos);
+}
+
+const uint8_t &buffer_view::write(size_t i) const {
+ size_t pos = i + write_offset_;
+
+ assert(pos < buffer_->write_composite_length());
+
+ return buffer_->write(pos);
+}
+
+error buffer_view::write_require_length(size_t bytes) {
+ return buffer_->write_require_length(bytes + write_offset_);
+}
+
+size_t buffer_view::read_offset() const { return read_offset_; }
+
+size_t buffer_view::write_offset() const { return write_offset_; }
+
+ring_buffer::ring_buffer() : read_position_{0}, write_position_{0} {
+ buffer_.resize(RING_BUFFER_MAX_SIZE);
+}
+
+ring_buffer::ring_buffer(size_t size) : read_position_{0}, write_position_{0} {
+ buffer_.resize(size);
+}
+
+size_t ring_buffer::read_position() const { return read_position_; }
+
+/*
+ * If write is ahead of read it is a simple distance, but if read ist ahead of
+ * write then there are two segments
+ *
+ */
+size_t ring_buffer::read_composite_length() const {
+ return write_position() < read_position()
+ ? buffer_.size() - (read_position() - write_position())
+ : (write_reached_read_ ? buffer_.size()
+ : write_position() - read_position());
+}
+
+/*
+ * If write is ahead then it's the simple distance again. If read is ahead it's
+ * until the end of the buffer/segment
+ */
+size_t ring_buffer::read_segment_length(size_t offset) const {
+ size_t read_composite = read_composite_length();
+ assert(offset <= read_composite);
+ offset = std::min(offset, read_composite);
+ size_t remaining = read_composite - offset;
+
+ size_t read_offset = read_position() + offset;
+ read_offset = read_offset >= buffer_.size() ? read_offset - buffer_.size()
+ : read_offset;
+
+ // case 1 write is located before read and reached read
+ // then offset can be used normally
+ // case 2 write is located at read, but read reached write
+ // then it is set to zero by readCompositeLength()
+ // case 3 write is located after read
+ // since std::min you can use simple subtraction
+ if (write_position() < read_offset) {
+ return buffer_.size() - read_offset;
+ }
+
+ if (write_position() == read_offset) {
+ if (remaining > 0) {
+ return buffer_.size() - read_offset;
+ } else {
+ return 0;
+ }
+ }
+
+ return write_position() - read_offset;
+}
+
+void ring_buffer::read_advance(size_t bytes) {
+ size_t read_composite = read_composite_length();
+
+ assert(bytes <= read_composite);
+ bytes = std::min(bytes, read_composite);
+ size_t advanced = read_position_ + bytes;
+ read_position_ = advanced >= buffer_.size() ? advanced - buffer_.size()
+ : advanced;
+ write_reached_read_ = bytes > 0 ? false : write_reached_read_;
+}
+
+uint8_t &ring_buffer::read(size_t i) {
+ assert(i < read_composite_length());
+ size_t pos = read_position_ + i;
+ pos = pos >= buffer_.size() ? pos - buffer_.size() : pos;
+ return buffer_[pos];
+}
+
+const uint8_t &ring_buffer::read(size_t i) const {
+ assert(i < read_composite_length());
+ size_t pos = read_position_ + i;
+ pos = pos >= buffer_.size() ? pos - buffer_.size() : pos;
+ return buffer_[pos];
+}
+
+size_t ring_buffer::write_position() const { return write_position_; }
+
+size_t ring_buffer::write_composite_length() const {
+ return read_position() > write_position()
+ ? (read_position() - write_position())
+ : (write_reached_read_
+ ? 0
+ : buffer_.size() - (write_position() - read_position()));
+}
+
+size_t ring_buffer::write_segment_length(size_t offset) const {
+ size_t write_composite = write_composite_length();
+ assert(offset <= write_composite);
+ offset = std::min(offset, write_composite);
+
+ size_t write_offset = write_position() + offset;
+ write_offset = write_offset >= buffer_.size()
+ ? write_offset - buffer_.size()
+ : write_offset;
+
+ if (read_position_ > write_offset) {
+ return read_position_ - write_offset;
+ }
+
+ if (write_reached_read_) {
+ return 0;
+ }
+
+ return buffer_.size() - write_offset;
+}
+
+void ring_buffer::write_advance(size_t bytes) {
+ assert(bytes <= write_composite_length());
+ size_t advanced = write_position_ + bytes;
+ write_position_ = advanced >= buffer_.size() ? advanced - buffer_.size()
+ : advanced;
+
+ write_reached_read_ =
+ (write_position_ == read_position_ && bytes > 0 ? true : false);
+}
+
+uint8_t &ring_buffer::write(size_t i) {
+ assert(i < write_composite_length());
+ size_t pos = write_position_ + i;
+ pos = pos >= buffer_.size() ? pos - buffer_.size() : pos;
+ return buffer_[pos];
+}
+
+const uint8_t &ring_buffer::write(size_t i) const {
+ assert(i < write_composite_length());
+ size_t pos = write_position_ + i;
+ pos = pos >= buffer_.size() ? pos - buffer_.size() : pos;
+ return buffer_[pos];
+}
+/*
+ Error RingBuffer::increaseSize(size_t size){
+ size_t old_size = buffer.size();
+ size_t new_size = old_size + size;
+ buffer.resize(new_size);
+ if(readPosition() > writePosition() || (readPosition() ==
+ writePosition() && write_reached_read)){ size_t remaining = old_size -
+ writePosition(); size_t real_remaining = 0; while(remaining > 0){ size_t
+ segment = std::min(remaining, size); memcpy(&buffer[new_size-segment],
+ &buffer[old_size-segment], segment); remaining -= segment; size -= segment;
+ old_size -= segment;
+ new_size -= segment;
+ }
+ }
+
+ return noError();
+ }
+*/
+error ring_buffer::write_require_length(size_t bytes) {
+ size_t write_remain = write_composite_length();
+ if (bytes > write_remain) {
+ return make_error<err::buffer_exhausted>();
+ }
+ return no_error();
+}
+
+array_buffer::array_buffer(size_t size)
+ : read_position_{0}, write_position_{0} {
+ buffer_.resize(size);
+}
+
+size_t array_buffer::read_position() const { return read_position_; }
+
+size_t array_buffer::read_composite_length() const {
+ return write_position_ - read_position_;
+}
+
+size_t array_buffer::read_segment_length(size_t offset) const {
+ size_t read_composite = read_composite_length();
+ assert(offset <= read_composite);
+
+ offset = std::min(read_composite, offset);
+ size_t read_offset = read_position_ + offset;
+
+ return write_position_ - read_offset;
+}
+
+void array_buffer::read_advance(size_t bytes) {
+ assert(bytes <= read_composite_length());
+ read_position_ += bytes;
+}
+
+uint8_t &array_buffer::read(size_t i) {
+ assert(i < read_composite_length());
+
+ return buffer_[i + read_position_];
+}
+
+const uint8_t &array_buffer::read(size_t i) const {
+ assert(i + read_position_ < buffer_.size());
+
+ return buffer_[i + read_position_];
+}
+
+size_t array_buffer::write_position() const { return write_position_; }
+
+size_t array_buffer::write_composite_length() const {
+ assert(write_position_ <= buffer_.size());
+ return buffer_.size() - write_position_;
+}
+
+size_t array_buffer::write_segment_length(size_t offset) const {
+ assert(write_position_ <= buffer_.size());
+ size_t write_composite = write_composite_length();
+
+ assert(offset <= write_composite);
+ offset = std::min(write_composite, offset);
+ size_t write_offset = write_position_ + offset;
+
+ return buffer_.size() - write_offset;
+}
+
+void array_buffer::write_advance(size_t bytes) {
+ assert(bytes <= write_composite_length());
+ write_position_ += bytes;
+}
+
+uint8_t &array_buffer::write(size_t i) {
+ assert(i < write_composite_length());
+ return buffer_[i + write_position_];
+}
+
+const uint8_t &array_buffer::write(size_t i) const {
+ assert(i < write_composite_length());
+ return buffer_[i + write_position_];
+}
+error array_buffer::write_require_length(size_t bytes) {
+ size_t write_remain = write_composite_length();
+ if (bytes > write_remain) {
+ return make_error<err::buffer_exhausted>();
+ }
+ return no_error();
+}
+
+} // namespace saw
diff --git a/modules/core/buffer.h b/modules/core/buffer.h
new file mode 100644
index 0000000..f0cf76e
--- /dev/null
+++ b/modules/core/buffer.h
@@ -0,0 +1,199 @@
+#pragma once
+
+#include "error.h"
+
+#include <array>
+#include <cstdint>
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
+
+namespace saw {
+/*
+ * Access class to reduce templated BufferSegments bloat
+ */
+class buffer {
+protected:
+ ~buffer() = default;
+
+public:
+ virtual size_t read_position() const = 0;
+ virtual size_t read_composite_length() const = 0;
+ virtual size_t read_segment_length(size_t offset = 0) const = 0;
+ virtual void read_advance(size_t bytes) = 0;
+
+ virtual uint8_t &read(size_t i = 0) = 0;
+ virtual const uint8_t &read(size_t i = 0) const = 0;
+
+ virtual size_t write_position() const = 0;
+ virtual size_t write_composite_length() const = 0;
+ virtual size_t write_segment_length(size_t offset = 0) const = 0;
+ virtual void write_advance(size_t bytes) = 0;
+
+ virtual uint8_t &write(size_t i = 0) = 0;
+ virtual const uint8_t &write(size_t i = 0) const = 0;
+
+ /*
+ * Sometime buffers need to grow with a little more control
+ * than with push and pop for more efficient calls.
+ * There is nothing you can do if read hasn't been filled, but at
+ * least write can be increased if it is demanded.
+ */
+ virtual error write_require_length(size_t bytes) = 0;
+
+ error push(const uint8_t &value);
+ error push(const uint8_t &buffer, size_t size);
+ error pop(uint8_t &value);
+ error pop(uint8_t &buffer, size_t size);
+
+};
+
+/**
+ * Converts a buffer to a string for convenience cases
+ */
+std::string convert_to_string(const buffer& buf);
+
+/**
+ *
+ */
+
+/*
+ * A viewer class for buffers.
+ * Working on the reference buffer invalidates the buffer view
+ */
+class buffer_view : public buffer {
+private:
+ buffer *buffer_;
+ size_t read_offset_;
+ size_t write_offset_;
+
+public:
+ buffer_view(buffer &);
+
+ size_t read_position() const override;
+ size_t read_composite_length() const override;
+ size_t read_segment_length(size_t offset = 0) const override;
+ void read_advance(size_t bytes) override;
+
+ uint8_t &read(size_t i = 0) override;
+ const uint8_t &read(size_t i = 0) const override;
+
+ size_t write_position() const override;
+ size_t write_composite_length() const override;
+ size_t write_segment_length(size_t offset = 0) const override;
+ void write_advance(size_t bytes) override;
+
+ uint8_t &write(size_t i = 0) override;
+ const uint8_t &write(size_t i = 0) const override;
+
+ error write_require_length(size_t bytes) override;
+
+ size_t read_offset() const;
+ size_t write_offset() const;
+};
+
+/*
+ * Buffer size meant for default allocation size of the ringbuffer since
+ * this class currently doesn't support proper resizing
+ */
+constexpr size_t RING_BUFFER_MAX_SIZE = 4096;
+/*
+ * Buffer wrapping around if read caught up
+ */
+class ring_buffer final : public buffer {
+private:
+ std::vector<uint8_t> buffer_;
+ size_t read_position_;
+ size_t write_position_;
+ bool write_reached_read_ = false;
+
+public:
+ ring_buffer();
+ ring_buffer(size_t size);
+
+ inline size_t size() const { return buffer_.size(); }
+
+ inline uint8_t &operator[](size_t i) { return buffer_[i]; }
+ inline const uint8_t &operator[](size_t i) const { return buffer_[i]; }
+
+ size_t read_position() const override;
+ size_t read_composite_length() const override;
+ size_t read_segment_length(size_t offset = 0) const override;
+ void read_advance(size_t bytes) override;
+
+ uint8_t &read(size_t i = 0) override;
+ const uint8_t &read(size_t i = 0) const override;
+
+ size_t write_position() const override;
+ size_t write_composite_length() const override;
+ size_t write_segment_length(size_t offset = 0) const override;
+ void write_advance(size_t bytes) override;
+
+ uint8_t &write(size_t i = 0) override;
+ const uint8_t &write(size_t i = 0) const override;
+
+ error write_require_length(size_t bytes) override;
+};
+
+/*
+ * One time buffer
+ */
+class array_buffer : public buffer {
+private:
+ std::vector<uint8_t> buffer_;
+
+ size_t read_position_;
+ size_t write_position_;
+
+public:
+ array_buffer(size_t size);
+
+ size_t read_position() const override;
+ size_t read_composite_length() const override;
+ size_t read_segment_length(size_t offset = 0) const override;
+ void read_advance(size_t bytes) override;
+
+ uint8_t &read(size_t i = 0) override;
+ const uint8_t &read(size_t i = 0) const override;
+
+ size_t write_position() const override;
+ size_t write_composite_length() const override;
+ size_t write_segment_length(size_t offset = 0) const override;
+ void write_advance(size_t bytes) override;
+
+ uint8_t &write(size_t i = 0) override;
+ const uint8_t &write(size_t i = 0) const override;
+
+ error write_require_length(size_t bytes) override;
+};
+
+class chain_array_buffer : public buffer {
+private:
+ std::deque<array_buffer> buffer_;
+
+ size_t read_position_;
+ size_t write_position_;
+
+public:
+ chain_array_buffer();
+
+ size_t read_position() const override;
+ size_t read_composite_length() const override;
+ size_t read_segment_length(size_t offset = 0) const override;
+ void read_advance(size_t bytes) override;
+
+ uint8_t &read(size_t i = 0) override;
+ const uint8_t &read(size_t i = 0) const override;
+
+ size_t write_position() const override;
+ size_t write_composite_length() const override;
+ size_t write_segment_length(size_t offset = 0) const override;
+ void write_advance(size_t bytes) override;
+
+ uint8_t &write(size_t i = 0) override;
+ const uint8_t &write(size_t i = 0) const override;
+
+ error write_require_length(size_t bytes) override;
+};
+} // namespace saw
diff --git a/modules/core/common.h b/modules/core/common.h
new file mode 100644
index 0000000..a06c238
--- /dev/null
+++ b/modules/core/common.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace saw {
+
+#define SAW_CONCAT_(x, y) x##y
+#define SAW_CONCAT(x, y) SAW_CONCAT_(x, y)
+#define SAW_UNIQUE_NAME(prefix) SAW_CONCAT(prefix, __LINE__)
+
+#define SAW_FORBID_COPY(classname) \
+ classname(const classname &) = delete; \
+ classname &operator=(const classname &) = delete
+
+#define SAW_FORBID_MOVE(classname) \
+ classname(classname &&) = delete; \
+ classname &operator=(classname &&) = delete
+
+#define SAW_DEFAULT_COPY(classname) \
+ classname(const classname &) = default; \
+ classname &operator=(const classname &) = default
+
+#define SAW_DEFAULT_MOVE(classname) \
+ classname(classname &&) = default; \
+ classname &operator=(classname &&) = default
+
+// In case of C++20
+#define SAW_ASSERT(expression) \
+ assert(expression); \
+ if (!(expression)) [[unlikely]]
+
+template <typename T> using maybe = std::optional<T>;
+
+template <typename T> using own = std::unique_ptr<T>;
+
+template <typename T> using our = std::shared_ptr<T>;
+
+template <typename T> using lent = std::weak_ptr<T>;
+
+template <typename T, class... Args> own<T> heap(Args &&...args) {
+ return own<T>(new T(std::forward<Args>(args)...));
+}
+
+template <typename T, class... Args> our<T> share(Args &&...args) {
+ return std::make_shared<T>(std::forward<Args>(args)...);
+}
+
+template <typename T> T instance() noexcept;
+
+template <typename Func, typename T> struct return_type_helper {
+ typedef decltype(instance<Func>()(instance<T>())) Type;
+};
+template <typename Func> struct return_type_helper<Func, void> {
+ typedef decltype(instance<Func>()()) Type;
+};
+
+template <typename Func, typename T>
+using return_type = typename return_type_helper<Func, T>::Type;
+
+struct void_t {};
+
+template <typename T> struct void_fix { typedef T Type; };
+template <> struct void_fix<void> { typedef void_t Type; };
+template <typename T> using fix_void = typename void_fix<T>::Type;
+
+template <typename T> struct void_unfix { typedef T Type; };
+template <> struct void_unfix<void_t> { typedef void Type; };
+template <typename T> using unfix_void = typename void_unfix<T>::Type;
+
+template <typename... T> constexpr bool always_false = false;
+
+} // namespace saw
diff --git a/modules/core/error.cpp b/modules/core/error.cpp
new file mode 100644
index 0000000..360e628
--- /dev/null
+++ b/modules/core/error.cpp
@@ -0,0 +1,156 @@
+#include "error.h"
+
+namespace saw {
+error::error(error::code code_, bool is_critical__)
+ : error_code_{static_cast<error::code>(code_)}, is_critical_{is_critical__} {}
+
+error::error(error::code code_, bool is_critical__, const std::string_view &msg)
+ :
+ error_code_{static_cast<error::code>(code_)}
+ , is_critical_{is_critical__}, error_message_{msg}{}
+
+error::error(error &&error)
+ :
+ error_code_{std::move(error.error_code_)}
+ , is_critical_{std::move(error.is_critical_)}
+ , error_message_{std::move(error.error_message_)}{}
+
+const std::string_view error::get_category() const {
+ auto& reg = impl::get_error_registry();
+
+ auto eov = reg.search_category(error_code_);
+ SAW_ASSERT(eov.is_value()){
+ return "Error category not found. Report this error to the forstio maintainer";
+ }
+
+ return eov.get_value();
+}
+
+const std::string_view error::get_message() const {
+ return std::visit(
+ [this](auto &&arg) -> const std::string_view {
+ using T = std::decay_t<decltype(arg)>;
+
+ if constexpr (std::is_same_v<T, std::string>) {
+ return std::string_view{arg};
+ } else if constexpr (std::is_same_v<T, std::string_view>) {
+ return arg;
+ } else {
+ return "Error in class Error. Good luck :)";
+ }
+ },
+ error_message_);
+}
+
+bool error::failed() const {
+ return !this->is_type<err::no_error>();
+}
+
+bool error::is_critical() const {
+ return is_critical_;
+}
+
+bool error::is_recoverable() const {
+ return !is_critical_;
+}
+
+error error::copy_error() const {
+ auto copy_error_code = error_code_;
+ error error{copy_error_code, is_critical_};
+
+ try {
+ error.error_message_ = error_message_;
+ } catch (const std::bad_alloc &) {
+ error.error_message_ =
+ std::string_view{"Error while copying Error string. Out of memory"};
+ }
+
+ return error;
+}
+
+error::code error::get_id() const { return error_code_; }
+
+namespace impl {
+error_registry& get_error_registry() {
+ static own<error_registry> reg = nullptr;
+ if(!reg){
+ reg = heap<error_registry>();
+ }
+
+ assert(reg);
+ return *reg;
+}
+}
+
+error no_error(){
+ return make_error<err::no_error>();
+}
+
+namespace impl {
+error_registry::error_registry():
+ infos_{
+ {
+ err::no_error::description,
+ err::no_error::is_critical
+ },
+ {
+ err::not_found::description,
+ err::not_found::is_critical
+ },
+ {
+ err::out_of_memory::description,
+ err::out_of_memory::is_critical
+ }
+ }
+{}
+
+error_or<const std::string_view> error_registry::search_category(const error::code& id) const {
+ if( id >= infos_.size()){
+ return make_error<err::not_found>();
+ }
+
+ return infos_.at(id).description;
+}
+
+error_or<error::code> error_registry::search_id(const std::string_view& desc)const{
+ /**
+ * Search the index in the vector
+ */
+ size_t i{};
+ size_t info_max_size = std::min<std::size_t>(infos_.size(), std::numeric_limits<error::code>::max());
+ for(i = 0; i < info_max_size; ++i){
+ if(infos_.at(i).description == desc){
+ break;
+ }
+ }
+
+ if(i == info_max_size){
+ return make_error<err::not_found>();
+ }
+
+ return static_cast<error::code>(i);
+}
+
+error_or<error::code> error_registry::search_or_register_id(const std::string_view& desc, bool is_critical){
+ auto err_or_id = search_id(desc);
+
+ if(err_or_id.is_value()){
+ return err_or_id.get_value();
+ }
+
+ auto& err = err_or_id.get_error();
+
+ if(err.is_type<err::not_found>()){
+ size_t new_index = infos_.size();
+ if(new_index == std::numeric_limits<error::code>::max()){
+ return make_error<err::out_of_memory>("Error registry ids are exhausted");
+ }
+ infos_.emplace_back(error_info{desc, is_critical});
+ return static_cast<error::code>(new_index);
+ }
+
+ return std::move(err);
+}
+}
+
+} // namespace saw
diff --git a/modules/core/error.h b/modules/core/error.h
new file mode 100644
index 0000000..e816734
--- /dev/null
+++ b/modules/core/error.h
@@ -0,0 +1,248 @@
+#pragma once
+
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <string_view>
+#include <variant>
+#include <vector>
+
+#include <cassert>
+
+#include "common.h"
+
+namespace saw {
+/**
+ * Utility class for generating errors. Has a base distinction between
+ * critical and recoverable errors. Additional code ids can be provided to the
+ * constructor if additional distinctions are necessary.
+ */
+class error {
+public:
+ using code = uint32_t;
+private:
+ code error_code_;
+ bool is_critical_;
+ std::variant<std::string_view, std::string> error_message_;
+
+public:
+ error(error::code id, bool is_critical);
+ error(error::code id, bool is_critical, const std::string_view &msg);
+ error(error &&error);
+
+ SAW_FORBID_COPY(error);
+
+ error &operator=(error &&) = default;
+
+ const std::string_view get_message() const;
+
+ const std::string_view get_category() const;
+
+ bool failed() const;
+
+ bool is_critical() const;
+ bool is_recoverable() const;
+
+ /**
+ * Replaces the copy constructor. We need this since we want to explicitly copy and not implicitly
+ */
+ error copy_error() const;
+
+ code get_id() const;
+
+ template<typename T>
+ bool is_type() const;
+};
+
+template<typename T>
+class error_or;
+
+namespace impl {
+
+class error_registry {
+private:
+ struct error_info {
+ error_info() = delete;
+ error_info(const std::string_view& d_, bool critical_):description{d_}, is_critical{critical_}{}
+
+ std::string_view description;
+ bool is_critical;
+ };
+
+ std::vector<error_info> infos_;
+public:
+ error_registry();
+
+ SAW_FORBID_COPY(error_registry);
+ SAW_FORBID_MOVE(error_registry);
+
+ error_or<const std::string_view> search_category(const error::code& id) const;
+
+ error_or<error::code> search_id(const std::string_view& desc) const;
+
+ error_or<error::code> search_or_register_id(const std::string_view& desc, bool is_critical);
+};
+
+error_registry& get_error_registry();
+
+template<typename T>
+error::code get_template_id(){
+ static error::code id = std::numeric_limits<error::code>::max();
+
+ if(id == std::numeric_limits<error::code>::max()){
+ auto& reg = get_error_registry();
+
+ auto err_or_id = reg.search_or_register_id(T::description, T::is_critical);
+ if(err_or_id.is_error()){
+ return std::numeric_limits<error::code>::max();
+ }
+
+ id = err_or_id.get_value();
+ }
+
+ return id;
+}
+}
+
+template<typename T> error make_error(const std::string_view& generic){
+ error::code id = impl::get_template_id<T>();
+
+ return error{id, T::is_critical, generic};
+}
+
+template<typename T> error make_error(){
+ error::code id = impl::get_template_id<T>();
+
+ return error{id, T::is_critical};
+}
+
+error make_error(error::code code, const std::string_view &generic);
+
+
+namespace err {
+struct no_error {
+ static constexpr std::string_view description = "No error has occured";
+ static constexpr bool is_critical = false;
+};
+
+struct recoverable {
+ static constexpr std::string_view description = "Generic recoverable error";
+ static constexpr bool is_critical = false;
+};
+
+struct critical {
+ static constexpr std::string_view description = "Generic critical error";
+ static constexpr bool is_critical = true;
+};
+
+struct buffer_exhausted {
+ static constexpr std::string_view description = "Buffer Exhausted";
+ static constexpr bool is_critical = false;
+};
+
+struct not_found {
+ static constexpr std::string_view description = "Not found";
+ static constexpr bool is_critical = false;
+};
+
+struct out_of_memory {
+ static constexpr std::string_view description = "Out of memory";
+ static constexpr bool is_critical = true;
+};
+
+struct invalid_state {
+ static constexpr std::string_view description = "Invalid state";
+ static constexpr bool is_critical = true;
+};
+
+struct not_supported {
+ static constexpr std::string_view description = "Not supported";
+ static constexpr bool is_critical = false;
+};
+
+struct not_implemented {
+ static constexpr std::string_view description = "Not implemented";
+ static constexpr bool is_critical = true;
+};
+
+struct already_exists {
+ static constexpr std::string_view description = "Already exists";
+ static constexpr bool is_critical = false;
+};
+}
+
+/**
+ * Shorthand for no error happened
+ */
+error no_error();
+
+/**
+ * Exception alternative. Since I code without exceptions this class is
+ * essentially a kind of exception replacement.
+ */
+template <typename T> class error_or;
+
+class error_or_value {
+public:
+ virtual ~error_or_value() = default;
+
+ template <typename T> error_or<unfix_void<T>> &as() {
+ return static_cast<error_or<unfix_void<T>> &>(*this);
+ }
+
+ template <typename T> const error_or<unfix_void<T>> &as() const {
+ return static_cast<const error_or<unfix_void<T>> &>(*this);
+ }
+};
+
+template <typename T> class error_or final : public error_or_value {
+private:
+ std::variant<error, fix_void<T>> value_or_error_;
+
+ static_assert(!std::is_same_v<T, void_t>,
+ "Don't use internal private types");
+
+public:
+ error_or():value_or_error_{fix_void<T>{}}{}
+ error_or(const fix_void<T> &value) : value_or_error_{value} {}
+
+ error_or(fix_void<T> &&value) : value_or_error_{std::move(value)} {}
+
+ error_or(const error &error) : value_or_error_{error} {}
+ error_or(error &&error) : value_or_error_{std::move(error)} {}
+
+ bool is_value() const {
+ return std::holds_alternative<fix_void<T>>(value_or_error_);
+ }
+
+ bool is_error() const {
+ return std::holds_alternative<class error>(value_or_error_);
+ }
+
+ class error &get_error() {
+ return std::get<class error>(value_or_error_);
+ }
+
+ const class error &get_error() const {
+ return std::get<class error>(value_or_error_);
+ }
+
+ fix_void<T> &get_value() { return std::get<fix_void<T>>(value_or_error_); }
+
+ const fix_void<T> &get_value() const {
+ return std::get<fix_void<T>>(value_or_error_);
+ }
+};
+
+template <typename T> class error_or<error_or<T>> {
+private:
+ error_or() = delete;
+};
+
+template<typename T>
+bool error::is_type() const {
+
+ return error_code_ == impl::get_template_id<T>();
+}
+
+} // namespace saw
diff --git a/modules/core/id.h b/modules/core/id.h
new file mode 100644
index 0000000..d836648
--- /dev/null
+++ b/modules/core/id.h
@@ -0,0 +1,54 @@
+#pragma once
+
+namespace saw {
+/**
+ * ID class which is tied to it's representing class
+ */
+template<typename T>
+class id {
+private:
+ /**
+ * Alias for the value type representing the ID
+ */
+ using type = uint64_t;
+
+ /**
+ * The low level value
+ */
+ type value_;
+public:
+ /**
+ * Basic constructor for the id class
+ */
+ id(type val):
+ value_{val}
+ {}
+
+ SAW_DEFAULT_COPY(id);
+ SAW_DEFAULT_MOVE(id);
+
+ /**
+ * Equal operator for the id.
+ * Returns true if equal, false otherwise.
+ */
+ bool operator==(const id<T>& rhs) const {
+ return value_ == rhs.value_;
+ }
+
+ /**
+ * Unequal operator for the id.
+ * Returns false if equal, true otherwise.
+ */
+ bool operator!=(const id<T>& rhs) const {
+ return !(*this == rhs);
+ }
+
+ /**
+ * Returns a const ref of the underlying base type.
+ * Mostly used for internal purposes.
+ */
+ const type& get_value() const {
+ return value_;
+ }
+};
+}
diff --git a/modules/core/id_map.h b/modules/core/id_map.h
new file mode 100644
index 0000000..d8329cf
--- /dev/null
+++ b/modules/core/id_map.h
@@ -0,0 +1,137 @@
+#pragma once
+
+#include "id.h"
+#include "error.h"
+
+#include <deque>
+
+namespace saw {
+/**
+ * Fast random access id based container.
+ *
+ * Access - O(1)
+ * Insert - O(1)
+ * Erase - O(n) ? Dunno
+ */
+template<typename T>
+class id_map final {
+private:
+ /**
+ * Container which stores the primary data
+ */
+ std::vector<T> data_;
+ /**
+ * Container which tracks free'd/fragmented elements within the
+ * main container
+ */
+ std::deque<id<T>> free_ids_;
+
+private:
+ /**
+ * Tries to reduce top ids
+ */
+ void reduce_free_ids() noexcept {
+ for(;;){
+ if(free_ids_.empty()){
+ break;
+ }
+
+ if((free_ids_.front().get_value() + 1) < data_.size()){
+ break;
+ }
+
+ /**
+ * Can this throw?
+ */
+ free_ids_.pop_front();
+ }
+ }
+public:
+ /**
+ * Default constructor
+ */
+ id_map() = default;
+
+ SAW_FORBID_COPY(id_map);
+ SAW_DEFAULT_MOVE(id_map);
+
+ /**
+ * Inserts an element into the container and returns either an id on success
+ * or an error on failure.
+ */
+ error_or<id<T>> insert(T val) noexcept {
+ /// @todo Fix size_t and id base type
+ if(free_ids_.empty()){
+ try {
+ size_t i = data_.size();
+ data_.emplace_back(std::move(val));
+ return saw::id<T>{i};
+ } catch(std::exception& e) {
+ return make_error<err::out_of_memory>();
+ }
+ } else {
+ auto f_id = std::move(free_ids_.back());
+ free_ids_.pop_back();
+ data_.at(f_id.get_value()) = std::move(val);
+ return f_id;
+ }
+
+ exit(-1);
+ // Dummy return since this is not reachable
+ return make_error<err::critical>();
+ }
+
+ /**
+ * Erase a value at this id.
+ */
+ error_or<void> erase(const id<T>& val) noexcept {
+ /**
+ * If id is bigger than the available vector then return an error.
+ */
+ if(val.get_value() >= data_.size()){
+ return make_error<err::not_found>("ID is too large");
+ }
+
+ /**
+ * Check if it's the highest ID. Then we can just try to reduce the highest
+ * IDs.
+ */
+ if((val.get_value() + 1) == data_.size()){
+ data_.pop_back();
+ this->reduce_free_ids();
+ if(data_.size()*2 <= data_.capacity()){
+ try {
+ data_.shrink_to_fit();
+ }catch(std::exception& e){
+ return make_error<err::out_of_memory>();
+ }
+ }
+ return void_t{};
+ }
+
+ /**
+ * Check if ID already exists with the free IDs.
+ * This would mean that a double free has occured.
+ */
+ auto find_id = std::find(free_ids_.begin(), free_ids_.end(), val);
+ if(find_id != free_ids_.end()){
+ return make_error<err::not_found>("ID value has already been freed");
+ }
+
+ /**
+ * Insert id into deque and sort it.
+ */
+ try {
+ free_ids_.push_back(val);
+ } catch(std::exception& e){
+ return make_error<err::out_of_memory>();
+ }
+ std::stable_sort(free_ids_.begin(), free_ids_.end(),
+ [](const id<T>& left, const id<T>& right) -> bool {
+ return left.get_value() > right.get_value();
+ }
+ );
+ return void_t{};
+ }
+};
+}
diff --git a/modules/core/mcts.h b/modules/core/mcts.h
new file mode 100644
index 0000000..8a8f5ea
--- /dev/null
+++ b/modules/core/mcts.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "tree.h"
+
+namespace saw {
+template<typename T>
+class mcts_tree {
+private:
+ struct value {
+ uint64_t numerater;
+ uint64_t denominater;
+ T state;
+
+ value() = default;
+ value(T st):
+ numerater{0},
+ denominater{0},
+ state{std::move(st)}
+ {}
+ };
+
+ tree_container<value, mcts_tree<T>> data_;
+public:
+ mcts_tree() = default{
+ data_.add(value{});
+ }
+
+ mcts_tree(T state){
+ data_.add(value{std::move(state)});
+ }
+
+ size_t size() const {
+ return data_.size() - 1;
+ }
+
+ T& get_state(){
+ return data_.at(0).get_value().state;
+ }
+
+ const T& get_state() const {
+ return data_.at(0).get_value().state;
+ }
+
+ mcts_tree<T>& get_tree(size_t i){
+ return data_.at(i+1).get_tree();
+ }
+
+ const mcts_tree<T>& get_tree(size_t i) const {
+ return data_.at(i+1).get_tree();
+ }
+};
+}
diff --git a/modules/core/platonic.h b/modules/core/platonic.h
new file mode 100644
index 0000000..eefe99f
--- /dev/null
+++ b/modules/core/platonic.h
@@ -0,0 +1,103 @@
+#pragma once
+
+#include "error.h"
+
+namespace saw {
+namespace impl {
+/**
+ *
+ */
+template<typename Prec, uint8_t N>
+struct platonic_helper {
+ static_assert(always_false<Prec,N>, "Unsupported platonic body. Alternatively it's not a platonic body");
+};
+
+template<typename Prec>
+struct platonic_helper<Prec,4u> {
+ static constexpr surface_edges = 3u;
+/*
+ static constexpr std::array<std::array<Prec,3u>, 4u> normals = {
+ {0.0, 0.0, -1.0}, // 1
+ {}, // 2
+ {}, // 3
+ {} // 4
+ };
+*/
+};
+
+template<typename Prec>
+struct platonic_helper<Prec,6u> {
+ static constexpr surface_edges = 4u;
+
+ static constexpr std::array<std::array<Prec,3u>, 6u> normals = {
+ { 1.0, 0.0, 0.0}, // 1
+ {-1.0, 0.0, 0.0}, // 2
+ { 0.0, 1.0, 0.0}, // 3
+ { 0.0,-1.0, 0.0}, // 4
+ { 0.0, 0.0, 1.0}, // 5
+ { 0.0, 0.0,-1.0} // 6
+ };
+};
+
+template<typename Prec>
+struct platonic_helper<Prec,20u> {
+ static constexpr uint8_t surface_edges = 3u;
+/*
+ static constexpr std::array<std::array<Prec,3u>, 20u> normals = {
+ {}, // 1
+ {}, // 2
+ {}, // 3
+ {}, // 4
+ {}, // 5
+ {}, // 6
+ {}, // 7
+ {}, // 8
+ {}, // 9
+ {}, // 10
+ {}, // 11
+ {}, // 12
+ {}, // 13
+ {}, // 14
+ {}, // 15
+ {}, // 16
+ {}, // 17
+ {}, // 18
+ {}, // 19
+ {} // 20
+ };
+*/
+};
+}
+/**
+ * Container for describing each platonic body with
+ * helpers describing the orientation of each body.
+ */
+template<typename T, typename Prec, uint8_t N>
+class platonic {
+private:
+ /**
+ * Storage for the surfaces
+ */
+ std::array<T,N> surfaces_;
+public:
+ constexpr uint8_t get_surface_edge_size() constexpr {
+ return platonic_helper<T,N>::surface_edges;
+ }
+
+ constexpr uint8_t get_surface_size() constexpr {
+ return N;
+ }
+
+ T& at(uint8_t i){
+ return surface_.at(i);
+ }
+
+ const T& at(uint8_t i) const {
+ return surface_.at(i);
+ }
+
+ constexpr std::array<Prec, 3>& get_surface_normal(size_t i) constexpr {
+
+ }
+};
+}
diff --git a/modules/core/string_literal.h b/modules/core/string_literal.h
new file mode 100644
index 0000000..ccc8f49
--- /dev/null
+++ b/modules/core/string_literal.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <array>
+#include <string_view>
+
+namespace saw {
+/**
+ * Helper object which creates a templated string from the provided string
+ * literal. It guarantees compile time uniqueness and thus allows using strings
+ * in template parameters.
+ */
+template <class CharT, size_t N> class string_literal {
+public:
+ static_assert(N > 0, "string_literal needs a null terminator");
+
+ constexpr string_literal(const CharT (&input)[N]) noexcept {
+ for (size_t i = 0; i < N; ++i) {
+ data[i] = input[i];
+ }
+ }
+
+ std::array<CharT, N> data{};
+
+ constexpr std::string_view view() const noexcept {
+ return std::string_view{data.data()};
+ }
+
+ constexpr bool
+ operator==(const string_literal<CharT, N> &) const noexcept = default;
+
+ template <class CharTR, size_t NR>
+ constexpr bool
+ operator==(const string_literal<CharTR, NR> &) const noexcept {
+ return false;
+ }
+
+ template<size_t NR>
+ constexpr string_literal<CharT, N+NR-1> operator+(const string_literal<CharT, NR>& rhs) const noexcept {
+ CharT sum[N+NR-1];
+
+ // The weird i+1 happens due to needing to skip the '\0' terminator
+ for(size_t i = 0; (i+1) < N; ++i){
+ sum[i] = data[i];
+ }
+ for(size_t i = 0; i < NR; ++i){
+ sum[i+N-1] = rhs.data[i];
+ }
+
+ return string_literal<CharT, N+NR-1>{sum};
+ }
+};
+
+template <typename T, T... Chars>
+constexpr string_literal<T, sizeof...(Chars) + 1u> operator""_sl() {
+ return string_literal<T, sizeof...(Chars) + 1u>{{Chars..., '\0'}};
+}
+} // namespace saw
diff --git a/modules/core/templates.h b/modules/core/templates.h
new file mode 100644
index 0000000..2eb0f7e
--- /dev/null
+++ b/modules/core/templates.h
@@ -0,0 +1,150 @@
+#pragma once
+
+#include "string_literal.h"
+
+namespace saw {
+
+template <class T, class... TL> struct parameter_pack_index;
+
+template <class T, class... TL> struct parameter_pack_index<T, T, TL...> {
+ static constexpr size_t value = 0u;
+};
+
+template <class T, class TL0, class... TL>
+struct parameter_pack_index<T, TL0, TL...> {
+ static constexpr size_t value =
+ 1u + parameter_pack_index<T, TL...>::value;
+};
+
+template <size_t N, class... T> struct parameter_pack_type {
+ static_assert(always_false<T...>, "Should've been caught by the specializations");
+};
+
+template <class TN, class... T> struct parameter_pack_type<0, TN, T...> {
+ using type = TN;
+};
+
+template <size_t N, class TN, class... T>
+struct parameter_pack_type<N, TN, T...> {
+ static_assert(sizeof...(T) > 0, "Exhausted parameters");
+ static_assert(N > 0, "Invalid number. Should've been caught");
+ using type = typename parameter_pack_type<N - 1, T...>::type;
+};
+/*
+ * Nightmare inducing compiler problems found here. Somehow non-type
+ * string_literals cannot be resolved as non-type primitive template values can.
+ * This is the workaround
+ */
+template <string_literal V, string_literal Key0, string_literal... Keys>
+struct parameter_key_pack_index_helper {
+ static constexpr size_t value =
+ (V == Key0)
+ ? (0u)
+ : (1u + parameter_key_pack_index_helper<V, Keys...>::value);
+};
+
+template <string_literal V, string_literal Key0>
+struct parameter_key_pack_index_helper<V, Key0> {
+ static constexpr size_t value = (V == Key0) ? (0u) : (1u);
+};
+
+template <string_literal V, string_literal... Keys>
+struct parameter_key_pack_index {
+ static constexpr size_t value =
+ parameter_key_pack_index_helper<V, Keys...>::value;
+ static_assert(value < sizeof...(Keys),
+ "Provided string_literal doesn't exist in searched list");
+};
+
+template <size_t i, size_t s, string_literal Key0, string_literal... Keys>
+struct parameter_key_pack_type_helper {
+ static constexpr string_literal literal = parameter_key_pack_type_helper<i, s+1, Keys...>::literal;
+};
+
+template <size_t i, string_literal Key0, string_literal... Keys>
+struct parameter_key_pack_type_helper<i, i, Key0, Keys...> {
+ static constexpr string_literal literal = Key0;
+};
+
+template <size_t i, string_literal... Keys>
+struct parameter_key_pack_type {
+ static constexpr string_literal literal = parameter_key_pack_type_helper<i, 0, Keys...>::literal;
+
+ static_assert(i < sizeof...(Keys), "Provided index is too large for list");
+};
+
+template<std::size_t i, std::size_t s, typename T, T V0, T... VN>
+struct parameter_pack_value_helper {
+ static constexpr T value = parameter_pack_value_helper<i, s+1, T, VN...>::value;
+};
+
+template<std::size_t i, typename T, T V0, T... VN>
+struct parameter_pack_value_helper<i, i, T, V0, VN...> {
+ static constexpr T value = V0;
+};
+
+template<std::size_t i, typename T, T... Values>
+struct parameter_pack_value {
+ static constexpr T value = parameter_pack_value_helper<i, 0, T, Values...>::value;
+ static_assert(i < sizeof...(Values), "Provided index is too large for list");
+};
+
+template<typename T, T... V>
+struct ct_multiply;
+
+template<typename T>
+struct ct_multiply<T> {
+ static constexpr T value = 1;
+};
+
+template<typename T, T V0, T... VN>
+struct ct_multiply<T, V0, VN...> {
+ static constexpr T value = V0 * ct_multiply<T,VN...>::value;
+};
+
+namespace impl {
+template<typename T, size_t i>
+struct ct_convert_digits_table_helper {
+ static_assert(i <= 15, "Only conversion up to hex is supported");
+
+ static constexpr std::array<T, 16> table = {
+ '0', '1', '2', '3',
+ '4', '5', '6', '7',
+ '8', '9', 'A', 'B',
+ 'C', 'D', 'E', 'F'
+ };
+
+ static constexpr T value = table[i];
+};
+
+template<uint64_t Num, uint64_t Base, uint64_t... Digs>
+struct ct_convert_digits_helper {
+ static constexpr size_t size = ct_convert_digits_helper<Num / Base, Base, Num % Base, Digs...>::size;
+ static constexpr std::array<uint64_t, size> value = ct_convert_digits_helper<Num / Base, Base, Num % Base, Digs...>::value;
+ static constexpr string_literal literal = ct_convert_digits_helper<Num / Base, Base, Num % Base, Digs...>::literal;
+};
+
+template<uint64_t Base, uint64_t... Digs>
+struct ct_convert_digits_helper<0, Base, Digs...> {
+ static constexpr size_t size = sizeof...(Digs);
+ static constexpr std::array<uint64_t, size> value = {Digs...};
+ static constexpr string_literal literal = {{ct_convert_digits_table_helper<char, Digs>::value..., '\0'}};
+};
+
+template<uint64_t Base>
+struct ct_convert_digits_helper<0, Base> {
+ static constexpr size_t size = 0;
+ static constexpr std::array<uint64_t, 1> value = {0};
+ static constexpr string_literal literal = "0"_sl;
+};
+}
+
+template<uint64_t Num, uint64_t Base>
+struct ct_convert_to_digits {
+ static_assert(Base <= 16, "Only conversion up to hex is supported");
+
+ static constexpr size_t size = impl::ct_convert_digits_helper<Num, Base>::size;
+ static constexpr std::array<uint64_t, size> value = impl::ct_convert_digits_helper<Num, Base>::value;
+ static constexpr string_literal literal = impl::ct_convert_digits_helper<Num,Base>::literal;
+};
+}
diff --git a/modules/core/tree.h b/modules/core/tree.h
new file mode 100644
index 0000000..68fa20a
--- /dev/null
+++ b/modules/core/tree.h
@@ -0,0 +1,248 @@
+#pragma once
+
+#include <variant>
+#include <vector>
+
+#include "error.h"
+
+namespace saw {
+/**
+ * Container with a simplistic approach to a branch
+ */
+template<typename T, typename Tree>
+class branch;
+
+/**
+ * Tree object holding branches.
+ *
+ * The name comes from the fact a tree is acting as a node while the branch class is the
+ * edge to a leaf or other nodes. A tree holds the branches while the branch either has
+ * a leaf or another sub tree.
+ */
+template<typename T, typename Tree>
+class tree_container final {
+private:
+ /**
+ * Object holding the treeed branch instances
+ */
+ std::vector<branch<T,Tree>> children_;
+public:
+ /**
+ * Default constructor
+ */
+ tree_container() = default;
+
+ /**
+ * Destructor
+ */
+ ~tree_container() = default;
+
+ SAW_FORBID_COPY(tree_container);
+ SAW_DEFAULT_MOVE(tree_container);
+
+ /**
+ * Reserve space for siz elements
+ */
+ error_or<void> reserve(std::size_t siz){
+ try{
+ children_.reserve(siz);
+ }catch(const std::exception& e){
+ (void) e;
+
+ return make_error<err::out_of_memory>();
+ }
+
+ return void_t{};
+ }
+
+ /**
+ * Add a branch with a leaf attached to the tree
+ */
+ error_or<std::size_t> add(T leaf) {
+ std::size_t index = size();
+ try {
+ /**
+ * Technically we're adding a leaf on a branch
+ */
+ children_.emplace_back(std::move(leaf));
+ }catch(const std::exception& e){
+ (void)e;
+
+ return make_error<err::critical>();
+ }
+
+ return index;
+ }
+
+ /**
+ * Add a branch to the tree with a tree attached
+ */
+ error_or<std::size_t> add() {
+ std::size_t index = size();
+ try {
+
+ children_.emplace_back(Tree{});
+ }catch(const std::exception& e){
+ (void)e;
+
+ return make_error<err::critical>();
+ }
+
+ return index;
+ }
+
+ /**
+ * Returns the amount of branches contained within this tree level
+ */
+ std::size_t size() const {
+ return children_.size();
+ }
+
+ /**
+ * Returns the branch at i
+ */
+ branch<T,Tree>& at(std::size_t i){
+ return children_.at(i);
+ }
+
+ /**
+ * Returns the branch at i
+ */
+ const branch<T,Tree>& at(std::size_t i) const {
+ return children_.at(i);
+ }
+};
+
+template<typename T, typename Tree>
+class branch final {
+private:
+ using type = std::variant<Tree,T>;
+ type tov_;
+
+ /**
+ * We're friend classing the tree since it's way easier this way and the branch and tree
+ * class are intertwined heavily anyway.
+ */
+public:
+ /**
+ *
+ */
+ branch():tov_{Tree{}}{}
+
+ branch(Tree nd):tov_{std::move(nd)}{}
+
+ branch(T val):tov_{std::move(val)}{}
+
+ SAW_FORBID_COPY(branch);
+ SAW_DEFAULT_MOVE(branch);
+
+ template<typename NT>
+ bool is() const {
+ return std::holds_alternative<NT>(tov_);
+ }
+
+ bool is_tree() const {
+ return std::holds_alternative<Tree>(tov_);
+ }
+
+ bool is_value() const {
+ return std::holds_alternative<T>(tov_);
+ }
+
+ template<typename NT>
+ NT& get() {
+ return std::get<NT>(tov_);
+ }
+
+ template<typename NT>
+ const NT& get() const {
+ return std::get<NT>(tov_);
+ }
+
+ Tree& get_tree(){
+ return std::get<Tree>(tov_);
+ }
+
+ const Tree& get_tree() const {
+ return std::get<Tree>(tov_);
+ }
+
+ T& get_value(){
+ return std::get<T>(tov_);
+ }
+
+ const T& get_value() const {
+ return std::get<T>(tov_);
+ }
+
+ template<typename NT>
+ error_or<NT> extract(){
+ if(!is<NT>()){
+ return make_error<err::invalid_state>();
+ }
+
+ NT nd = std::move(std::get<NT>(tov_));
+ tov_ = Tree{};
+
+ return nd;
+ }
+
+ template<typename NT>
+ error_or<NT> replace(type nd){
+ auto eon = extract<NT>();
+ if(eon.is_error()){
+ return eon;
+ }
+
+ tov_ = std::move(nd);
+
+ return eon;
+ }
+
+ error_or<Tree> extract_tree() {
+ return extract<Tree>();
+ }
+
+ error_or<Tree> replace_tree(type nd){
+ return replace<Tree>(std::move(nd));
+ }
+
+ error_or<T> extract_value() {
+ return extract<T>();
+ }
+
+ error_or<T> replace_value(type nd){
+ return replace<T>(std::move(nd));
+ }
+};
+
+template<typename T>
+class tree {
+ private:
+ tree_container<T,tree<T>> data_;
+ public:
+ error_or<void> reserve(std::size_t size){
+ return data_.reserve(size);
+ }
+
+ size_t size() const {
+ return data_.size();
+ }
+
+ error_or<size_t> add() {
+ return data_.add();
+ }
+
+ error_or<size_t> add(T leaf){
+ return data_.add(std::move(leaf));
+ }
+
+ branch<T, tree<T>>& at(size_t i){
+ return data_.at(i);
+ }
+
+ const branch<T, tree<T>>& at(size_t i) const {
+ return data_.at(i);
+ }
+};
+}