diff options
author | Claudius "keldu" Holeksa <mail@keldu.de> | 2023-12-04 12:18:14 +0100 |
---|---|---|
committer | Claudius "keldu" Holeksa <mail@keldu.de> | 2023-12-04 12:18:14 +0100 |
commit | a14896f9ed209dd3f9597722e5a5697bd7dbf531 (patch) | |
tree | 089ca5cbbd206d1921f8f6b53292f5bc1902ca5c /modules/core | |
parent | 84ecdcbca9e55b1f57fbb832e12ff4fdbb86e7c9 (diff) |
meta: Renamed folder containing source
Diffstat (limited to 'modules/core')
-rw-r--r-- | modules/core/.nix/derivation.nix | 21 | ||||
-rw-r--r-- | modules/core/SConscript | 42 | ||||
-rw-r--r-- | modules/core/SConstruct | 66 | ||||
-rw-r--r-- | modules/core/array.h | 96 | ||||
-rw-r--r-- | modules/core/buffer.cpp | 436 | ||||
-rw-r--r-- | modules/core/buffer.h | 199 | ||||
-rw-r--r-- | modules/core/common.h | 75 | ||||
-rw-r--r-- | modules/core/error.cpp | 156 | ||||
-rw-r--r-- | modules/core/error.h | 248 | ||||
-rw-r--r-- | modules/core/id.h | 54 | ||||
-rw-r--r-- | modules/core/id_map.h | 137 | ||||
-rw-r--r-- | modules/core/mcts.h | 52 | ||||
-rw-r--r-- | modules/core/platonic.h | 103 | ||||
-rw-r--r-- | modules/core/string_literal.h | 57 | ||||
-rw-r--r-- | modules/core/templates.h | 150 | ||||
-rw-r--r-- | modules/core/tree.h | 248 |
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); + } +}; +} |