summaryrefslogtreecommitdiff
path: root/forstio
diff options
context:
space:
mode:
Diffstat (limited to 'forstio')
-rw-r--r--forstio/SConscript7
-rw-r--r--forstio/core/SConscript37
-rw-r--r--forstio/core/buffer.cpp434
-rw-r--r--forstio/core/buffer.h195
-rw-r--r--forstio/core/common.h75
-rw-r--r--forstio/core/error.cpp73
-rw-r--r--forstio/core/error.h149
-rw-r--r--forstio/core/string_literal.h40
8 files changed, 1010 insertions, 0 deletions
diff --git a/forstio/SConscript b/forstio/SConscript
new file mode 100644
index 0000000..08ac0f0
--- /dev/null
+++ b/forstio/SConscript
@@ -0,0 +1,7 @@
+#!/bin/false
+
+Import('env')
+
+# Export to other libs
+Export('env');
+SConscript('core/SConscript');
diff --git a/forstio/core/SConscript b/forstio/core/SConscript
new file mode 100644
index 0000000..184eed1
--- /dev/null
+++ b/forstio/core/SConscript
@@ -0,0 +1,37 @@
+#!/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, env.core_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, env.core_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.Alias('library', ['library_core']);
+
+# Install
+env.Install('$prefix/lib/', [core_env.library_shared, core_env.library_static]);
+env.Install('$prefix/include/forstio/', [core_env.headers]);
diff --git a/forstio/core/buffer.cpp b/forstio/core/buffer.cpp
new file mode 100644
index 0000000..2f1a6b5
--- /dev/null
+++ b/forstio/core/buffer.cpp
@@ -0,0 +1,434 @@
+#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 recoverable_error("Buffer too small");
+ }
+ 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 recoverable_error("Buffer too small");
+ }
+ 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 recoverable_error("Buffer too small");
+ }
+ return no_error();
+}
+
+std::string buffer::to_string() const {
+ std::ostringstream oss;
+ for (size_t i = 0; i < read_composite_length(); ++i) {
+ oss << read(i);
+ }
+ return oss.str();
+}
+
+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();
+}
+
+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 recoverable_error("Buffer too small");
+ }
+ 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 recoverable_error("Buffer too small");
+ }
+ return no_error();
+}
+
+} // namespace saw
diff --git a/forstio/core/buffer.h b/forstio/core/buffer.h
new file mode 100644
index 0000000..4485ff1
--- /dev/null
+++ b/forstio/core/buffer.h
@@ -0,0 +1,195 @@
+#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);
+
+ /*
+ * Subject to change
+ */
+ std::string to_string() const;
+ std::string to_hex() const;
+};
+
+/*
+ * 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/forstio/core/common.h b/forstio/core/common.h
new file mode 100644
index 0000000..a06c238
--- /dev/null
+++ b/forstio/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/forstio/core/error.cpp b/forstio/core/error.cpp
new file mode 100644
index 0000000..757e6a8
--- /dev/null
+++ b/forstio/core/error.cpp
@@ -0,0 +1,73 @@
+#include "error.h"
+
+namespace saw {
+error::error() : error_{static_cast<error::code>(0)} {}
+
+error::error(const std::string_view &msg, error::code code)
+ : error_message_{msg}, error_{static_cast<error::code>(code)} {}
+
+error::error(std::string &&msg, error::code code)
+ : error_message_{std::move(msg)}, error_{static_cast<error::code>(code)} {}
+
+error::error(error &&error)
+ : error_message_{std::move(error.error_message_)}, error_{std::move(
+ error.error_)} {}
+
+const std::string_view error::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 static_cast<std::underlying_type_t<error::code>>(error_) != 0;
+}
+
+bool error::is_critical() const {
+ return static_cast<std::underlying_type_t<error::code>>(error_) < 0;
+}
+
+bool error::is_recoverable() const {
+ return static_cast<std::underlying_type_t<error::code>>(error_) > 0;
+}
+
+error error::copy_error() const {
+ error error;
+ error.error_ = error_;
+ 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::id() const { return static_cast<error::code>(error_); }
+
+error make_error(const std::string_view &generic, error::code code) {
+ return error{generic, code};
+}
+
+error critical_error(const std::string_view &generic, error::code c) {
+ return make_error(generic, c);
+}
+
+error recoverable_error(const std::string_view &generic, error::code c) {
+ return make_error(generic, c);
+}
+
+error no_error() { return error{}; }
+
+} // namespace saw
diff --git a/forstio/core/error.h b/forstio/core/error.h
new file mode 100644
index 0000000..d8a5ae2
--- /dev/null
+++ b/forstio/core/error.h
@@ -0,0 +1,149 @@
+#pragma once
+
+#include <string>
+#include <string_view>
+#include <variant>
+
+#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:
+ enum class code : int16_t {
+ GenericCritical = -1,
+ GenericRecoverable = 1,
+ Disconnected = -99,
+ Exhausted = -98
+ };
+
+private:
+ std::variant<std::string_view, std::string> error_message_;
+ code error_;
+
+public:
+ error();
+ error(const std::string_view &msg, error::code id);
+ error(std::string &&msg, error::code id);
+ error(error &&error);
+
+ SAW_FORBID_COPY(error);
+
+ error &operator=(error &&) = default;
+
+ const std::string_view message() const;
+ bool failed() const;
+
+ bool is_critical() const;
+ bool is_recoverable() const;
+
+ error copy_error() const;
+
+ code id() const;
+};
+
+error make_error(const std::string_view &generic, error::code c);
+
+template <typename Formatter>
+error make_error(const Formatter &formatter, error::code code,
+ const std::string_view &generic) {
+ try {
+ std::string error_msg = formatter();
+ return error{std::move(error_msg), code};
+ } catch (std::bad_alloc &) {
+ return error{generic, code};
+ }
+}
+
+error critical_error(const std::string_view &generic,
+ error::code c = error::code::GenericCritical);
+
+template <typename Formatter>
+error critical_error(const Formatter &formatter,
+ const std::string_view &generic,
+ error::code c = error::code::GenericCritical) {
+ return make_error(formatter, c, generic);
+}
+
+error recoverable_error(const std::string_view &generic,
+ error::code c = error::code::GenericRecoverable);
+
+template <typename Formatter>
+error recoverable_error(const Formatter &formatter,
+ const std::string_view &generic,
+ error::code c = error::code::GenericRecoverable) {
+ return make_error(formatter, c, generic);
+}
+
+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() = default;
+ 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 &error() {
+ return std::get<class error>(value_or_error_);
+ }
+
+ const class error &error() const {
+ return std::get<class error>(value_or_error_);
+ }
+
+ fix_void<T> &value() { return std::get<fix_void<T>>(value_or_error_); }
+
+ const fix_void<T> &value() const {
+ return std::get<fix_void<T>>(value_or_error_);
+ }
+};
+
+template <typename T> class error_or<error_or<T>> {
+private:
+ error_or() = delete;
+};
+
+} // namespace saw
diff --git a/forstio/core/string_literal.h b/forstio/core/string_literal.h
new file mode 100644
index 0000000..d530a54
--- /dev/null
+++ b/forstio/core/string_literal.h
@@ -0,0 +1,40 @@
+#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:
+ 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 <typename T, T... Chars>
+constexpr string_literal<T, sizeof...(Chars)> operator""_key() {
+ return string_literal<T, sizeof...(Chars) + 1u>{Chars..., '\0'};
+}
+} // namespace saw