From 76668792c4d05d86a89d4d4cad2cbcc43bbd671f Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 5 Jul 2022 12:44:57 +0200 Subject: [PATCH 01/14] Add interface for creating and loading snapshots snapshot creation is only possilbe via runner, because runner without global state wont work. The fact that the runner also snapshots all other runner with the same globals is a bit obscure and may should be changed, but since they are kind of related \_o_o_/ --- inkcpp/include/runner.h | 7 +++++++ inkcpp/include/snapshot.h | 18 ++++++++++++++++++ inkcpp/include/story.h | 15 ++++++++++++++- inkcpp/include/types.h | 3 ++- inkcpp/story_impl.cpp | 2 ++ inkcpp_cl/inkcpp_cl.cpp | 2 ++ 6 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 inkcpp/include/snapshot.h diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index 737553de..78c9c87f 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -3,6 +3,7 @@ #include "config.h" #include "system.h" #include "functional.h" +#include "types.h" #ifdef INK_ENABLE_UNREAL #include "Containers/UnrealString.h" @@ -65,6 +66,12 @@ namespace ink::runtime */ virtual char* getline_alloc() = 0; + /** + * @brief creates a snapshot containing the runner, globals and all other runners connected to the globals. + * @sa story::new_runner_from_snapshot, story::new_globals_from_snapshot + */ + virtual snapshot* create_snapshot() const = 0; + #ifdef INK_ENABLE_STL /** * Gets the next line of output using C++ STL string. diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h new file mode 100644 index 00000000..733906f3 --- /dev/null +++ b/inkcpp/include/snapshot.h @@ -0,0 +1,18 @@ +#pragma once + +#include "types.h" + +namespace ink::runtime +{ + class snapshot { + public: + snapshot() = delete; + virtual ~snapshot() = 0; + + static snapshot* from_binary(unsigned char* data, size_t length, bool freeOnDestroy = true); + static snapshot* from_file(const char* filename); + + virtual const char* get_data() const = 0; + virtual unsigned get_data_len() const = 0; + }; +} diff --git a/inkcpp/include/story.h b/inkcpp/include/story.h index 0e4bfbd4..9f7e4935 100644 --- a/inkcpp/include/story.h +++ b/inkcpp/include/story.h @@ -19,6 +19,7 @@ namespace ink::runtime class story { public: + virtual ~story() = 0; #pragma region Interface Methods /** * Creates a new global store @@ -30,6 +31,7 @@ namespace ink::runtime * @return managed pointer to a new global store */ virtual globals new_globals() = 0; + virtual globals new_globals_from_snapshot(const snapshot&) = 0; /** * Creates a new runner @@ -41,6 +43,17 @@ namespace ink::runtime * @return managed pointer to a new runner */ virtual runner new_runner(globals store = nullptr) = 0; + /** + * @brief reconstruct runner from a snapshot + * @attention runner must be snap_shotted from the same story + * @attention if globals is explicit set, + * make sure the globals are from the same snapshot as + * @attention if you snap_shotted a multiple runner with shared global + * please reconstruct it in the same fashion + * @param store can be set if explicit access to globals is required or multiple runner with a shared global are used + * @param idx if the snapshot was of a multiple runner one global situation load first the global, and then each runner with global set and increasing idx + */ + virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) = 0; #pragma endregion #pragma region Factory Methods @@ -71,4 +84,4 @@ namespace ink::runtime static story* from_binary(unsigned char* data, size_t length, bool freeOnDestroy = true); #pragma endregion }; -} \ No newline at end of file +} diff --git a/inkcpp/include/types.h b/inkcpp/include/types.h index 406078d8..cdb22801 100644 --- a/inkcpp/include/types.h +++ b/inkcpp/include/types.h @@ -11,7 +11,8 @@ namespace ink::runtime { class globals_interface; class runner_interface; + class snapshot; typedef story_ptr globals; typedef story_ptr runner; -} \ No newline at end of file +} diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index f83a7e57..8f96c54b 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -236,6 +236,8 @@ namespace ink::runtime::internal _list_meta = nullptr; _lists = nullptr; } + inkAssert(_header.endien == header::endian_types::same, + "different endien support not yet implemented"); diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 53a08209..27259dce 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -177,6 +177,8 @@ int main(int argc, const char** argv) break; } + delete myInk; + return 0; } catch (const std::exception& e) From 43c3d5bf50a833d107f26fc092431af5bc992ba8 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 7 Jul 2022 22:50:32 +0200 Subject: [PATCH 02/14] More snapshot magic --- inkcpp/array.h | 30 +- inkcpp/avl_array.h | 38 +- inkcpp/globals_impl.cpp | 22 +- inkcpp/globals_impl.h | 21 +- inkcpp/include/globals.h | 5 +- inkcpp/include/snapshot.h | 9 +- inkcpp/list_table.cpp | 8 + inkcpp/list_table.h | 7 +- inkcpp/output.cpp | 820 +++++++++++++++++++------------------- inkcpp/output.h | 7 +- inkcpp/random.h | 9 +- inkcpp/runner_impl.cpp | 83 ++-- inkcpp/runner_impl.h | 70 ++-- inkcpp/snapshot_impl.cpp | 83 ++++ inkcpp/snapshot_impl.h | 62 +++ inkcpp/story_impl.h | 2 + inkcpp/string_table.cpp | 10 + inkcpp/string_table.h | 12 +- inkcpp/value.h | 7 +- 19 files changed, 802 insertions(+), 503 deletions(-) create mode 100644 inkcpp/snapshot_impl.cpp create mode 100644 inkcpp/snapshot_impl.h diff --git a/inkcpp/array.h b/inkcpp/array.h index 9ad97afb..8b6cfddb 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -1,11 +1,12 @@ #pragma once #include "system.h" +#include "snapshot_impl.h" namespace ink::runtime::internal { template - class managed_array { + class managed_array : snapshot_interface { public: managed_array() : _capacity{initialCapacity}, _size{0}{ if constexpr (dynamic) { @@ -48,11 +49,28 @@ namespace ink::runtime::internal } void clear() { _size = 0; } void resize(size_t size) { - ink_assert(size <= _size, "Only allow to reduce size"); + if constexpr (dynamic) { + if (size > _capacity) { + extend(size); + } + } else { + ink_assert(size <= _size, "Only allow to reduce size"); + } _size = size; } - void extend(); + void extend(size_t capacity = 0); + + size_t snap(unsigned char* data, const snapper&) const override + { + unsigned char* ptr = data; + ptr = snap_write(ptr, &_size, data); + for(const T& e : *this) { + ptr = snap_write(ptr, e, data); + } + return ptr - data; + } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; private: if_t _static_data[dynamic ? 1 : initialCapacity]; @@ -62,10 +80,12 @@ namespace ink::runtime::internal }; template - void managed_array::extend() + void managed_array::extend(size_t capacity) { static_assert(dynamic, "Can only extend if array is dynamic!"); - size_t new_capacity = 1.5f * _capacity; + size_t new_capacity = capacity > _capacity + ? 1.5f * _capacity + : capacity; if (new_capacity < 5) { new_capacity = 5; } T* new_data = new T[new_capacity]; diff --git a/inkcpp/avl_array.h b/inkcpp/avl_array.h index 9690149a..ed925706 100644 --- a/inkcpp/avl_array.h +++ b/inkcpp/avl_array.h @@ -41,6 +41,8 @@ #ifndef _AVL_ARRAY_H_ #define _AVL_ARRAY_H_ +#include "system.h" + #include @@ -73,16 +75,20 @@ class avl_array static const size_type INVALID_IDX = Size; // iterator class - typedef class tag_avl_array_iterator + template + class tag_avl_array_iterator { - avl_array* instance_; // array instance + template + using if_t = ink::runtime::internal::if_t; + if_t + instance_; // array instance size_type idx_; // actual node friend avl_array; // avl_array may access index pointer public: // ctor - tag_avl_array_iterator(avl_array* instance = nullptr, size_type idx = 0U) + tag_avl_array_iterator(if_t instance = nullptr, size_type idx = 0U) : instance_(instance) , idx_(idx) { } @@ -101,15 +107,15 @@ class avl_array { return !(*this == rhs); } // dereference - access value - inline T& operator*() const + inline if_t operator*() const { return val(); } // access value - inline T& val() const + inline if_t val() const { return instance_->val_[idx_]; } // access key - inline Key& key() const + inline const Key& key() const { return instance_->key_[idx_]; } // preincrement @@ -152,7 +158,7 @@ class avl_array ++(*this); return _copy; } - } avl_array_iterator; + }; public: @@ -163,7 +169,8 @@ class avl_array typedef T& reference; typedef const T& const_reference; typedef Key key_type; - typedef avl_array_iterator iterator; + typedef tag_avl_array_iterator iterator; + typedef tag_avl_array_iterator const_iterator; // ctor @@ -184,9 +191,24 @@ class avl_array return iterator(this, i); } + inline const_iterator begin() const + { + size_type i = INVALID_IDX; + if (root_ != INVALID_IDX) { + // find smallest element, it's the farthest node left from root + for (i = root_; child_[i].left != INVALID_IDX; i = child_[i].left); + } + return const_iterator(this, i); + } + inline iterator end() { return iterator(this, INVALID_IDX); } + inline const_iterator end() const + { + return const_iterator(this, INVALID_IDX); + } + // capacity inline size_type size() const diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 1749366e..90e5cb90 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -1,17 +1,21 @@ #include "globals_impl.h" #include "story_impl.h" #include "runner_impl.h" +#include "snapshot_impl.h" + +#include namespace ink::runtime::internal { globals_impl::globals_impl(const story_impl* story) : _num_containers(story->num_containers()) - , _visit_counts(_num_containers) + , _visit_counts() , _owner(story) , _runners_start(nullptr) , _lists(story->list_meta(), story->get_header()) , _globals_initialized(false) { + _visit_counts.resize(_num_containers); if (_lists) { // initelize static lists const list_flag* flags = story->lists(); @@ -216,4 +220,20 @@ namespace ink::runtime::internal { _variables.forget(); } + + snapshot* globals_impl::create_snapshot() const + { + return new snapshot_impl(*this); + } + + size_t globals_impl::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr; + inkAssert(_num_containers == _visit_counts.size(), "Should be equal!"); + inkAssert(_globals_initialized, "Only support snapshot of globals with runner! or you don't need a snapshot for this state"); + ptr += _visit_counts.snap( data ? ptr : nullptr, snapper ); + ptr += _strings.snap( data ? ptr : nullptr, snapper ); + ptr += _lists.snap( data ? ptr : nullptr, snapper ); + return ptr - data; + } } diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index a2d9704b..70ab71ae 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -6,20 +6,27 @@ #include "string_table.h" #include "list_table.h" #include "stack.h" +#include "snapshot_impl.h" namespace ink::runtime::internal { class story_impl; class runner_impl; + class snapshot_impl; // Implementation of the global store - class globals_impl : public globals_interface + class globals_impl : public globals_interface, public snapshot_interface { + friend snapshot_impl; + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; public: // Initializes a new global store from the given story globals_impl(const story_impl*); virtual ~globals_impl() { } + snapshot* create_snapshot() const override; + protected: optional get_uint(hash_t name) const override; bool set_uint(hash_t name, uint32_t value) override; @@ -66,6 +73,7 @@ namespace ink::runtime::internal // gets the allocated string table inline string_table& strings() { return _strings; } + inline const string_table& strings() const { return _strings; } // gets list entries list_table& lists() { return _lists; } @@ -93,16 +101,7 @@ namespace ink::runtime::internal return !(*this == vc); } }; - class visit_counts{ - visit_count* _data; - size_t _len; - public: - visit_counts(size_t len) - : _data{new visit_count[len]}, _len{len} {} - size_t size() const { return _len; } - visit_count& operator[](size_t i) { return _data[i]; } - const visit_count& operator[](size_t i) const { return _data[i]; } - } _visit_counts; + managed_array _visit_counts; // Pointer back to owner story. const story_impl* const _owner; diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index ae6b1a97..c39bc9d6 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -4,8 +4,7 @@ namespace ink::runtime { - class globals_interface; - namespace internal { class globals_impl;} + class snapshot; /** * Represents a global store to be shared amongst ink runners. @@ -42,6 +41,8 @@ namespace ink::runtime return false; } + virtual snapshot* create_snapshot() const = 0; + virtual ~globals_interface() = default; protected: diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h index 733906f3..8d89c687 100644 --- a/inkcpp/include/snapshot.h +++ b/inkcpp/include/snapshot.h @@ -6,13 +6,14 @@ namespace ink::runtime { class snapshot { public: - snapshot() = delete; virtual ~snapshot() = 0; - static snapshot* from_binary(unsigned char* data, size_t length, bool freeOnDestroy = true); + static snapshot* from_binary(const unsigned char* data, size_t length, bool freeOnDestroy = true); +#ifdef INK_ENABLE_STL static snapshot* from_file(const char* filename); +#endif - virtual const char* get_data() const = 0; - virtual unsigned get_data_len() const = 0; + virtual const unsigned char* get_data() const = 0; + virtual size_t get_data_len() const = 0; }; } diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 06b57aa8..cf8a9cb5 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -650,5 +650,13 @@ namespace ink::runtime::internal } #endif + size_t list_table::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr += _data.snap(data ? ptr : nullptr, snapper); + ptr += _entry_state.snap(data ? ptr : nullptr, snapper); + return ptr - data; + } + } diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index 3ebd5497..d3396b29 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -2,6 +2,7 @@ #include "system.h" #include "array.h" +#include "snapshot_impl.h" #ifdef INK_ENABLE_STL #include @@ -24,7 +25,7 @@ namespace ink::runtime::internal } /// managed all list entries and list metadata - class list_table + class list_table : public snapshot_interface { using data_t = int; enum class state : char { @@ -77,6 +78,10 @@ namespace ink::runtime::internal */ char* toString(char* out, const list& l) const; + // snapshot interface implementation + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + /** special traitment when a list get assignet again * when a list get assigned and would have no origin, it gets the origin of the base with origin * eg. I072 diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 559de9ff..976faa54 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -8,500 +8,506 @@ #include #endif -namespace ink +namespace ink::runtime::internal { - namespace runtime + basic_stream::basic_stream(value* buffer, size_t len) + : _data(buffer), _max(len), _size(0), _save(~0) + {} + + void basic_stream::append(const value& in) { - namespace internal + // SPECIAL: Incoming newline + if (in.type() == value_type::newline && _size > 1) { - basic_stream::basic_stream(value* buffer, size_t len) - : _data(buffer), _max(len), _size(0), _save(~0) - {} + // If the end of the stream is a function start marker, we actually + // want to ignore this. Function start trimming. + if (_data[_size - 1].type() == value_type::func_start) + return; + } - void basic_stream::append(const value& in) - { - // SPECIAL: Incoming newline - if (in.type() == value_type::newline && _size > 1) - { - // If the end of the stream is a function start marker, we actually - // want to ignore this. Function start trimming. - if (_data[_size - 1].type() == value_type::func_start) - return; - } + // Ignore leading newlines + if (in.type() == value_type::newline && _size == 0) + return; - // Ignore leading newlines - if (in.type() == value_type::newline && _size == 0) - return; + // Add to data stream + inkAssert(_size < _max, "Output stream overflow"); + _data[_size++] = in; - // Add to data stream - inkAssert(_size < _max, "Output stream overflow"); - _data[_size++] = in; + // Special: Incoming glue. Trim whitespace/newlines prior + // This also applies when a function ends to trim trailing whitespace. + if ((in.type() == value_type::glue || in.type() == value_type::func_end) && _size > 1) + { + // Run backwards + size_t i = _size - 2; + while(true) + { + value& d = _data[i]; - // Special: Incoming glue. Trim whitespace/newlines prior - // This also applies when a function ends to trim trailing whitespace. - if ((in.type() == value_type::glue || in.type() == value_type::func_end) && _size > 1) - { - // Run backwards - size_t i = _size - 2; - while(true) - { - value& d = _data[i]; - - // Nullify newlines - if (d.type() == value_type::newline) { - d = value{}; - } - - // Nullify whitespace - else if ( d.type() == value_type::string - && is_whitespace(d.get())) - d = value{}; - - // If it's not a newline or whitespace, stop - else break; - - // If we've hit the end, break - if (i == 0) - break; - - // Move on to next element - i--; - } + // Nullify newlines + if (d.type() == value_type::newline) { + d = value{}; } - } - void basic_stream::append(const value* in, unsigned int length) - { - // TODO: Better way to bulk while still executing glue checks? - for (size_t i = 0; i < length; i++) - append(in[i]); - } + // Nullify whitespace + else if ( d.type() == value_type::string + && is_whitespace(d.get())) + d = value{}; - template - inline void write_char(OUT& output, char c) - { - static_assert(always_false::value, "Invalid output type"); - } + // If it's not a newline or whitespace, stop + else break; - template<> - inline void write_char(char*& output, char c) - { - (*output++) = c; - } + // If we've hit the end, break + if (i == 0) + break; - template<> - inline void write_char(std::stringstream& output, char c) - { - output.put(c); + // Move on to next element + i--; } + } + } - inline bool get_next(const value* list, size_t i, size_t size, const value** next) - { - while (i + 1 < size) - { - *next = &list[i + 1]; - value_type type = (*next)->type(); - if ((*next)->printable()) { return true; } - i++; - } + void basic_stream::append(const value* in, unsigned int length) + { + // TODO: Better way to bulk while still executing glue checks? + for (size_t i = 0; i < length; i++) + append(in[i]); + } - return false; - } + template + inline void write_char(OUT& output, char c) + { + static_assert(always_false::value, "Invalid output type"); + } - template - void basic_stream::copy_string(const char* str, size_t& dataIter, OUT& output) - { - while(*str != 0) { - write_char(output, *str++); - } - } + template<> + inline void write_char(char*& output, char c) + { + (*output++) = c; + } -#ifdef INK_ENABLE_STL - std::string basic_stream::get() - { - size_t start = find_start(); + template<> + inline void write_char(std::stringstream& output, char c) + { + output.put(c); + } - // Move up from marker - bool hasGlue = false, lastNewline = false; - std::stringstream str; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue, lastNewline)) - continue; - if (_data[i].printable()){ - _data[i].write(str, _lists_table); - } + inline bool get_next(const value* list, size_t i, size_t size, const value** next) + { + while (i + 1 < size) + { + *next = &list[i + 1]; + value_type type = (*next)->type(); + if ((*next)->printable()) { return true; } + i++; + } - } + return false; + } + + template + void basic_stream::copy_string(const char* str, size_t& dataIter, OUT& output) + { + while(*str != 0) { + write_char(output, *str++); + } + } - // Reset stream size to where we last held the marker - _size = start; +#ifdef INK_ENABLE_STL + std::string basic_stream::get() + { + size_t start = find_start(); - // Return processed string - // remove mulitple accourencies of ' ' - std::string result = str.str(); - auto end = clean_string(result.begin(), result.end()); - _last_char = *(end-1); - result.resize(end - result.begin() - (_last_char == ' ' ? 1 : 0)); - return result; + // Move up from marker + bool hasGlue = false, lastNewline = false; + std::stringstream str; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue, lastNewline)) + continue; + if (_data[i].printable()){ + _data[i].write(str, _lists_table); } -#endif -#ifdef INK_ENABLE_UNREAL - FString basic_stream::get() - { - size_t start = find_start(); - // TODO: Slow! FString concatonation. - // Is there really no equivilent of stringstream in Unreal? Some kind of String Builder? + } - // Move up from marker - bool hasGlue = false; - FString str; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue)) - continue; + // Reset stream size to where we last held the marker + _size = start; - switch (_data[i].type) - { - case value_type::int32: - str += FString::Printf(TEXT("%d"), _data[i].integer_value); - break; - case value_type::float32: - // TODO: Whitespace cleaning - str += FString::Printf(TEXT("%f"), _data[i].float_value); - break; - case value_type::string: - str += _data[i].string_val; - break; - case data_type::newline: - str += "\n"; - break; - default: - break; - } - } + // Return processed string + // remove mulitple accourencies of ' ' + std::string result = str.str(); + auto end = clean_string(result.begin(), result.end()); + _last_char = *(end-1); + result.resize(end - result.begin() - (_last_char == ' ' ? 1 : 0)); + return result; + } +#endif +#ifdef INK_ENABLE_UNREAL + FString basic_stream::get() + { + size_t start = find_start(); - // Reset stream size to where we last held the marker - _size = start; + // TODO: Slow! FString concatonation. + // Is there really no equivilent of stringstream in Unreal? Some kind of String Builder? - // Return processed string - return str; - } -#endif + // Move up from marker + bool hasGlue = false; + FString str; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue)) + continue; - int basic_stream::queued() const + switch (_data[i].type) { - size_t start = find_start(); - return _size - start; + case value_type::int32: + str += FString::Printf(TEXT("%d"), _data[i].integer_value); + break; + case value_type::float32: + // TODO: Whitespace cleaning + str += FString::Printf(TEXT("%f"), _data[i].float_value); + break; + case value_type::string: + str += _data[i].string_val; + break; + case data_type::newline: + str += "\n"; + break; + default: + break; } + } - const value& basic_stream::peek() const - { - inkAssert(_size > 0, "Attempting to peek empty stream!"); - return _data[_size - 1]; - } + // Reset stream size to where we last held the marker + _size = start; - void basic_stream::discard(size_t length) - { - // discard elements - _size -= length; - if (_size < 0) - _size = 0; - } + // Return processed string + return str; + } +#endif - void basic_stream::get(value* ptr, size_t length) - { - // Find start - size_t start = find_start(); + int basic_stream::queued() const + { + size_t start = find_start(); + return _size - start; + } - const value* end = ptr + length; - //inkAssert(_size - start < length, "Insufficient space in data array to store stream contents!"); + const value& basic_stream::peek() const + { + inkAssert(_size > 0, "Attempting to peek empty stream!"); + return _data[_size - 1]; + } - // Move up from marker - bool hasGlue = false, lastNewline = false; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue, lastNewline)) - continue; + void basic_stream::discard(size_t length) + { + // discard elements + _size -= length; + if (_size < 0) + _size = 0; + } - // Make sure we can fit the next element - inkAssert(ptr < end, "Insufficient space in data array to store stream contents!"); + void basic_stream::get(value* ptr, size_t length) + { + // Find start + size_t start = find_start(); - // Copy any value elements - if (_data[i].printable()) { - *(ptr++) = _data[i]; - } - } + const value* end = ptr + length; + //inkAssert(_size - start < length, "Insufficient space in data array to store stream contents!"); - // Reset stream size to where we last held the marker - _size = start; - } + // Move up from marker + bool hasGlue = false, lastNewline = false; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue, lastNewline)) + continue; - bool basic_stream::has_marker() const - { - // TODO: Cache? - for (size_t i = 0; i < _size; i++) - { - if (_data[i].type() == value_type::marker) - return true; - } + // Make sure we can fit the next element + inkAssert(ptr < end, "Insufficient space in data array to store stream contents!"); - return false; + // Copy any value elements + if (_data[i].printable()) { + *(ptr++) = _data[i]; } + } - bool basic_stream::ends_with(value_type type) const - { - if (_size == 0) - return false; + // Reset stream size to where we last held the marker + _size = start; + } - return _data[_size - 1].type() == type; - } + bool basic_stream::has_marker() const + { + // TODO: Cache? + for (size_t i = 0; i < _size; i++) + { + if (_data[i].type() == value_type::marker) + return true; + } - bool basic_stream::saved_ends_with(value_type type) const - { - inkAssert(_save != ~0, "Stream is not saved!"); + return false; + } - if (_save == 0) - return false; + bool basic_stream::ends_with(value_type type) const + { + if (_size == 0) + return false; - return _data[_save - 1].type() == type; - } + return _data[_size - 1].type() == type; + } - void basic_stream::save() - { - inkAssert(_save == ~0, "Can not save over existing save point!"); + bool basic_stream::saved_ends_with(value_type type) const + { + inkAssert(_save != ~0, "Stream is not saved!"); - // Save the current size - _save = _size; - } + if (_save == 0) + return false; - void basic_stream::restore() - { - inkAssert(_save != ~0, "No save point to restore!"); + return _data[_save - 1].type() == type; + } - // Restore size to saved position - _size = _save; - _save = ~0; - } + void basic_stream::save() + { + inkAssert(_save == ~0, "Can not save over existing save point!"); - void basic_stream::forget() - { - inkAssert(_save != ~0, "No save point to forget!"); + // Save the current size + _save = _size; + } - // Just null the save point and continue as normal - _save = ~0; - } - - template char* basic_stream::get_alloc(string_table& strings, list_table& lists); - template char* basic_stream::get_alloc(string_table& strings, list_table& lists); + void basic_stream::restore() + { + inkAssert(_save != ~0, "No save point to restore!"); - template - char* basic_stream::get_alloc(string_table& strings, list_table& lists) - { - size_t start = find_start(); + // Restore size to saved position + _size = _save; + _save = ~0; + } - // Two passes. First for length - size_t length = 0; - bool hasGlue = false, lastNewline = false; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue, lastNewline)) - continue; - ++length; // potenzial space to sperate - if (_data[i].printable()) { - switch(_data[i].type()) { - case value_type::list: - length += lists.stringLen(_data[i].get()); - break; - case value_type::list_flag: - length += lists.stringLen(_data[i].get()); - default: length += value_length(_data[i]); - } - } - } + void basic_stream::forget() + { + inkAssert(_save != ~0, "No save point to forget!"); - // Allocate - char* buffer = strings.create(length + 1); - char* end = buffer + length + 1; - char* ptr = buffer; - hasGlue = false; lastNewline = false; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue, lastNewline)) - continue; - if(!_data[i].printable()) { continue; } - switch (_data[i].type()) - { - case value_type::int32: - case value_type::float32: - case value_type::uint32: - // Convert to string and advance - toStr(ptr, end - ptr, _data[i]); - while (*ptr != 0) ptr++; + // Just null the save point and continue as normal + _save = ~0; + } + + template char* basic_stream::get_alloc(string_table& strings, list_table& lists); + template char* basic_stream::get_alloc(string_table& strings, list_table& lists); - break; - case value_type::string: - { - // Copy string and advance - const char* value = _data[i].get(); - copy_string(value, i, ptr); - } break; - case value_type::newline: - *ptr = '\n'; ptr++; - break; + template + char* basic_stream::get_alloc(string_table& strings, list_table& lists) + { + size_t start = find_start(); + + // Two passes. First for length + size_t length = 0; + bool hasGlue = false, lastNewline = false; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue, lastNewline)) + continue; + ++length; // potenzial space to sperate + if (_data[i].printable()) { + switch(_data[i].type()) { case value_type::list: - ptr = lists.toString(ptr, _data[i].get()); + length += lists.stringLen(_data[i].get()); break; case value_type::list_flag: - ptr = lists.toString(ptr, _data[i].get()); - break; - default: throw ink_exception("cant convert expression to string!"); - } + length += lists.stringLen(_data[i].get()); + default: length += value_length(_data[i]); } + } + } - // Make sure last character is a null - *ptr = 0; + // Allocate + char* buffer = strings.create(length + 1); + char* end = buffer + length + 1; + char* ptr = buffer; + hasGlue = false; lastNewline = false; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue, lastNewline)) + continue; + if(!_data[i].printable()) { continue; } + switch (_data[i].type()) + { + case value_type::int32: + case value_type::float32: + case value_type::uint32: + // Convert to string and advance + toStr(ptr, end - ptr, _data[i]); + while (*ptr != 0) ptr++; + + break; + case value_type::string: + { + // Copy string and advance + const char* value = _data[i].get(); + copy_string(value, i, ptr); + } break; + case value_type::newline: + *ptr = '\n'; ptr++; + break; + case value_type::list: + ptr = lists.toString(ptr, _data[i].get()); + break; + case value_type::list_flag: + ptr = lists.toString(ptr, _data[i].get()); + break; + default: throw ink_exception("cant convert expression to string!"); + } + } - // Reset stream size to where we last held the marker - _size = start; + // Make sure last character is a null + *ptr = 0; - // Return processed string - { - auto end = clean_string(buffer, buffer+length); - *end = 0; - _last_char = end[-1]; - if constexpr (RemoveTail) { - if (_last_char == ' ') { end[-1] = 0; } - } - } - return buffer; - } + // Reset stream size to where we last held the marker + _size = start; - size_t basic_stream::find_start() const - { - // Find marker (or start) - size_t start = _size; - while (start > 0) - { - start--; - if (_data[start].type() == value_type::marker) - break; - } + // Return processed string + { + auto end = clean_string(buffer, buffer+length); + *end = 0; + _last_char = end[-1]; + if constexpr (RemoveTail) { + if (_last_char == ' ') { end[-1] = 0; } + } + } + return buffer; + } - // Make sure we're not violating a save point - if (_save != ~0 && start < _save) { - // TODO: check if we don't reset save correct - // at some point we can modifiy the output even behind save (probally discard?) and push a new element -> invalid save point - // inkAssert(false, "Trying to access output stream prior to save point!"); - const_cast(*this).clear(); - } + size_t basic_stream::find_start() const + { + // Find marker (or start) + size_t start = _size; + while (start > 0) + { + start--; + if (_data[start].type() == value_type::marker) + break; + } - return start; - } + // Make sure we're not violating a save point + if (_save != ~0 && start < _save) { + // TODO: check if we don't reset save correct + // at some point we can modifiy the output even behind save (probally discard?) and push a new element -> invalid save point + // inkAssert(false, "Trying to access output stream prior to save point!"); + const_cast(*this).clear(); + } - bool basic_stream::should_skip(size_t iter, bool& hasGlue, bool& lastNewline) const + return start; + } + + bool basic_stream::should_skip(size_t iter, bool& hasGlue, bool& lastNewline) const + { + if (_data[iter].printable() + && _data[iter].type() != value_type::newline + && _data[iter].type() != value_type::string) { + lastNewline = false; + hasGlue = false; + } else { + switch (_data[iter].type()) + { + case value_type::newline: + if (lastNewline) + return true; + if (hasGlue) + return true; + lastNewline = true; + break; + case value_type::glue: + hasGlue = true; + break; + case value_type::string: { - if (_data[iter].printable() - && _data[iter].type() != value_type::newline - && _data[iter].type() != value_type::string) { - lastNewline = false; - hasGlue = false; - } else { - switch (_data[iter].type()) + lastNewline = false; + // an empty string don't count as glued I095 + for(const char* i=_data[iter].get(); + *i; ++i) { - case value_type::newline: - if (lastNewline) - return true; - if (hasGlue) - return true; - lastNewline = true; - break; - case value_type::glue: - hasGlue = true; - break; - case value_type::string: - { - lastNewline = false; - // an empty string don't count as glued I095 - for(const char* i=_data[iter].get(); - *i; ++i) - { - // isspace only supports characters in [0, UCHAR_MAX] - if (!isspace(static_cast(*i))) { - hasGlue = false; - break; - } - } - } break; - default: + // isspace only supports characters in [0, UCHAR_MAX] + if (!isspace(static_cast(*i))) { + hasGlue = false; break; } } - - return false; + } break; + default: + break; } + } - bool basic_stream::text_past_save() const - { - // Check if there is text past the save - for (size_t i = _save; i < _size; i++) - { - const value& d = _data[i]; - if (d.type() == value_type::string) - { - // TODO: Cache what counts as whitespace? - if (!is_whitespace(d.get(), false)) - return true; - } - } - - // No text - return false; - } + return false; + } - void basic_stream::clear() + bool basic_stream::text_past_save() const + { + // Check if there is text past the save + for (size_t i = _save; i < _size; i++) + { + const value& d = _data[i]; + if (d.type() == value_type::string) { - _save = ~0; - _size = 0; + // TODO: Cache what counts as whitespace? + if (!is_whitespace(d.get(), false)) + return true; } + } - void basic_stream::mark_strings(string_table& strings) const - { - // Find all allocated strings and mark them as used - for (int i = 0; i < _size; i++) - { - if (_data[i].type() == value_type::string) { - string_type str = _data[i].get(); - if (str.allocated) { - strings.mark_used(str.str); - } - } + // No text + return false; + } + + void basic_stream::clear() + { + _save = ~0; + _size = 0; + } + + void basic_stream::mark_strings(string_table& strings) const + { + // Find all allocated strings and mark them as used + for (int i = 0; i < _size; i++) + { + if (_data[i].type() == value_type::string) { + string_type str = _data[i].get(); + if (str.allocated) { + strings.mark_used(str.str); } } + } + } #ifdef INK_ENABLE_STL - std::ostream& operator<<(std::ostream& out, basic_stream& in) - { - out << in.get(); - return out; - } + std::ostream& operator<<(std::ostream& out, basic_stream& in) + { + out << in.get(); + return out; + } - basic_stream& operator>>(basic_stream& in, std::string& out) - { - out = in.get(); - return in; - } + basic_stream& operator>>(basic_stream& in, std::string& out) + { + out = in.get(); + return in; + } #endif #ifdef INK_ENABLE_UNREAL - basic_stream& operator>>(basic_stream& in, FString& out) - { - out = in.get(); - return in; - } + basic_stream& operator>>(basic_stream& in, FString& out) + { + out = in.get(); + return in; + } #endif - + size_t basic_stream::snap(unsigned char* data, const snapper& snapper) const + { + const string_table& strings = snapper.strings; + unsigned char* ptr = data; + ptr = snap_write(ptr, _last_char, data); + ptr = snap_write(ptr, _size, data); + ptr = snap_write(ptr, _save, data); + for(auto itr = _data; itr != _data + _size; ++itr) + { + ptr += itr->snap(data ? ptr : nullptr, snapper); } + return ptr - data; } } diff --git a/inkcpp/output.h b/inkcpp/output.h index 60663f8e..1185ee20 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -2,6 +2,7 @@ #include "value.h" #include "platform.h" +#include "snapshot_impl.h" namespace ink { @@ -11,7 +12,7 @@ namespace ink { class string_table; class list_table; - class basic_stream + class basic_stream : public snapshot_interface { protected: basic_stream(value*, size_t); @@ -99,6 +100,10 @@ namespace ink bool saved() const { return _save != ~0; } + // snapshot interface + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + private: size_t find_start() const; bool should_skip(size_t iter, bool& hasGlue, bool& lastNewline) const; diff --git a/inkcpp/random.h b/inkcpp/random.h index 995b7947..0738aa75 100644 --- a/inkcpp/random.h +++ b/inkcpp/random.h @@ -7,15 +7,15 @@ namespace ink::runtime::internal { * @brief pseudo random number generator based on Linear Congruential Generator. */ class prng { - static constexpr uint32_t C = 12345; - static constexpr uint32_t A = 1103515245; - static constexpr uint32_t M = 1<<31; + static constexpr uint64_t C = 12345; + static constexpr uint64_t A = 1103515245; + static constexpr uint64_t M = 1<<31; public: void srand(int32_t seed) { _x = seed; } uint32_t rand() { - _x = (A*_x+ C) % M; + _x = static_cast((A*_x+ C) % M); return _x; } int32_t rand(int32_t max) { @@ -23,6 +23,7 @@ namespace ink::runtime::internal { prod *= max; return static_cast(prod / M); } + uint32_t get_state() const { return _x; } private: uint32_t _x = 1337; }; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index ee51511a..499ae741 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -5,6 +5,7 @@ #include "globals_impl.h" #include "header.h" #include "string_utils.h" +#include "snapshot_impl.h" namespace ink::runtime { @@ -269,10 +270,10 @@ namespace ink::runtime::internal // Push next address onto the callstack { size_t address = _ptr - _story->instructions(); - _stack.push_frame(address, bEvaluationMode); - _ref_stack.push_frame(address, bEvaluationMode); + _stack.push_frame(address, _evaluation_mode); + _ref_stack.push_frame(address, _evaluation_mode); } - bEvaluationMode = false; // unset eval mode when enter function or tunnel + _evaluation_mode = false; // unset eval mode when enter function or tunnel // Do the jump inkAssert(_story->instructions() + target < _story->end(), "Diverting past end of story data!"); @@ -284,12 +285,12 @@ namespace ink::runtime::internal // Pop the callstack _ref_stack.fetch_values(_stack); frame_type type; - offset_t offset = _stack.pop_frame(&type,bEvaluationMode); + offset_t offset = _stack.pop_frame(&type,_evaluation_mode); _ref_stack.push_values(_stack); { frame_type t; bool eval; // TODO: write all refs to new frame offset_t o = _ref_stack.pop_frame(&t, eval); - inkAssert(o == offset && t == type && eval == bEvaluationMode, + inkAssert(o == offset && t == type && eval == _evaluation_mode, "_ref_stack and _stack should be in frame sync!"); } @@ -327,7 +328,7 @@ namespace ink::runtime::internal _backup(nullptr), _done(nullptr), _choices() { _ptr = _story->instructions(); - bEvaluationMode = false; + _evaluation_mode = false; // register with globals _globals->add_runner(this); @@ -525,6 +526,34 @@ namespace ink::runtime::internal return _tags[index]; } + snapshot* runner_impl::create_snapshot() const + { + return _globals->create_snapshot(); + } + + size_t runner_impl::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _ptr, data); + ptr = snap_write(ptr, _backup, data); + ptr = snap_write(ptr, _done, data); + ptr = snap_write(ptr, _rng.get_state(), data); + ptr = snap_write(ptr, _evaluation_mode, data); + ptr = snap_write(ptr, _saved_evaluation_mode, data); + ptr = snap_write(ptr, _eval, data); + ptr = snap_write(ptr, _saved, data); + ptr = snap_write(ptr, _is_falling, data); + ptr += _output.snap(data ? ptr : nullptr, snapper); + // _output + // _stack + // _ref_stack + // _threads + // _tags + // _choices + // _container + return ptr - data; + } + #ifdef INK_ENABLE_CSTD char* runner_impl::getline_alloc() { @@ -665,7 +694,7 @@ namespace ink::runtime::internal case Command::STR: { const char* str = read(); - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(value{}.set(str)); else _output << value{}.set(str); @@ -674,7 +703,7 @@ namespace ink::runtime::internal case Command::INT: { int val = read(); - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(value{}.set(val)); // TEST-CASE B006 don't print integers } @@ -682,7 +711,7 @@ namespace ink::runtime::internal case Command::BOOL: { bool val = read() ? true : false; - if(bEvaluationMode) + if(_evaluation_mode) _eval.push(value{}.set(val)); else _output << value{}.set(val); @@ -691,14 +720,14 @@ namespace ink::runtime::internal case Command::FLOAT: { float val = read(); - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(value{}.set(val)); // TEST-CASE B006 don't print floats } break; case Command::VALUE_POINTER: { hash_t val = read(); - if(bEvaluationMode) { + if(_evaluation_mode) { _eval.push(value{}.set(val, static_cast(flag) - 1)); } else { throw ink_exception("never conciderd what should happend here! (value pointer print)"); @@ -708,7 +737,7 @@ namespace ink::runtime::internal case Command::LIST: { list_table::list list(read()); - if(bEvaluationMode) + if(_evaluation_mode) _eval.push(value{}.set(list)); else { char* str = _globals->strings().create(_globals->lists().stringLen( @@ -720,7 +749,7 @@ namespace ink::runtime::internal break; case Command::DIVERT_VAL: { - inkAssert(bEvaluationMode, "Can not push divert value into the output stream!"); + inkAssert(_evaluation_mode, "Can not push divert value into the output stream!"); // Push the divert target onto the stack uint32_t target = read(); @@ -729,7 +758,7 @@ namespace ink::runtime::internal break; case Command::NEWLINE: { - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(values::newline); else _output << values::newline; @@ -737,7 +766,7 @@ namespace ink::runtime::internal break; case Command::GLUE: { - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(values::glue); else _output << values::glue; @@ -745,7 +774,7 @@ namespace ink::runtime::internal break; case Command::VOID: { - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(values::null); // TODO: void type? } break; @@ -865,8 +894,8 @@ namespace ink::runtime::internal // Push a thread frame so we can return easily // TODO We push ahead of a single divert. Is that correct in all cases....????? auto returnTo = _ptr + CommandSize; - _stack.push_frame(returnTo - _story->instructions(), bEvaluationMode); - _ref_stack.push_frame(returnTo - _story->instructions(), bEvaluationMode); + _stack.push_frame(returnTo - _story->instructions(), _evaluation_mode); + _ref_stack.push_frame(returnTo - _story->instructions(), _evaluation_mode); // Fork a new thread on the callstack thread_t thread = _stack.fork_thread(); @@ -939,10 +968,10 @@ namespace ink::runtime::internal // == Evaluation stack case Command::START_EVAL: - bEvaluationMode = true; + _evaluation_mode = true; break; case Command::END_EVAL: - bEvaluationMode = false; + _evaluation_mode = false; // Assert stack is empty? Is that necessary? break; @@ -970,15 +999,15 @@ namespace ink::runtime::internal } case Command::START_STR: { - inkAssert(bEvaluationMode, "Can not enter string mode while not in evaluation mode!"); - bEvaluationMode = false; + inkAssert(_evaluation_mode, "Can not enter string mode while not in evaluation mode!"); + _evaluation_mode = false; _output << values::marker; } break; case Command::END_STR: { // TODO: Assert we really had a marker on there? - inkAssert(!bEvaluationMode, "Must be in evaluation mode"); - bEvaluationMode = true; + inkAssert(!_evaluation_mode, "Must be in evaluation mode"); + _evaluation_mode = true; // Load value from output stream // Push onto stack @@ -1184,7 +1213,7 @@ namespace ink::runtime::internal _stack.clear(); _ref_stack.clear(); _threads.clear(); - bEvaluationMode = false; + _evaluation_mode = false; _saved = false; _choices.clear(); _ptr = nullptr; @@ -1219,7 +1248,7 @@ namespace ink::runtime::internal _eval.save(); _threads.save(); _backup_choice_len = _choices.size(); - bSavedEvaluationMode = bEvaluationMode; + _saved_evaluation_mode = _evaluation_mode; // Not doing this anymore. There can be lingering stack entries from function returns // inkAssert(_eval.is_empty(), "Can not save interpreter state while eval stack is not empty"); @@ -1238,7 +1267,7 @@ namespace ink::runtime::internal _eval.restore(); _threads.restore(); _choices.resize(_backup_choice_len); - bEvaluationMode = bSavedEvaluationMode; + _evaluation_mode = _saved_evaluation_mode; // Not doing this anymore. There can be lingering stack entries from function returns // inkAssert(_eval.is_empty(), "Can not save interpreter state while eval stack is not empty"); diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 7f208036..5f8eb15f 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -13,6 +13,7 @@ #include "list_table.h" #include "array.h" #include "random.h" +#include "snapshot_impl.h" #include "runner.h" #include "choice.h" @@ -23,8 +24,10 @@ namespace ink::runtime::internal { class story_impl; class globals_impl; + class snapshot_impl; - class runner_impl : public runner_interface + + class runner_impl : public runner_interface, public snapshot_interface { public: // Creates a new runner at the start of a loaded ink story @@ -55,6 +58,11 @@ namespace ink::runtime::internal virtual size_t num_tags() const override; virtual const char* get_tag(size_t index) const override; + snapshot* create_snapshot() const override; + + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + #ifdef INK_ENABLE_CSTD // c-style getline @@ -146,31 +154,7 @@ namespace ink::runtime::internal inline thread_t current_thread() const { return _threads.empty() ? ~0 : _threads.top(); } - private: - const story_impl* const _story; - story_ptr _globals; - executer _operations; - - // == State == - - // Instruction pointer - ip_t _ptr; - ip_t _backup; // backup pointer - ip_t _done; // when we last hit a done - - // Output stream - internal::stream _output; - - // Runtime stack. Used to store temporary variables and callstack - internal::stack _stack; - internal::stack _ref_stack; - - // Evaluation stack - bool bEvaluationMode = false; - internal::eval_stack _eval; - bool bSavedEvaluationMode = false; - - // Keeps track of what threads we're inside + public: class threads : public internal::simple_restorable_stack { using base = internal::simple_restorable_stack; static constexpr bool dynamic = config::limitThreadDepth < 0; @@ -222,14 +206,40 @@ namespace ink::runtime::internal using array_type = if_t, internal::fixed_restorable_array>; - template - void resize(size_t size, int) { (_threadDone.*A)(size); } - void resize(size_t size, long) {} + + void resize(size_t size, int) { _threadDone.resize(size); } managed_array _stack; array_type _threadDone; - } _threads; + }; + + private: + const story_impl* const _story; + story_ptr _globals; + executer _operations; + + // == State == + + // Instruction pointer + ip_t _ptr; + ip_t _backup; // backup pointer + ip_t _done; // when we last hit a done + + // Output stream + internal::stream _output; + + // Runtime stack. Used to store temporary variables and callstack + internal::stack _stack; + internal::stack _ref_stack; + + // Evaluation stack + bool _evaluation_mode = false; + internal::eval_stack _eval; + bool _saved_evaluation_mode = false; + + // Keeps track of what threads we're inside + threads _threads; // Choice list managed_array _choices; diff --git a/inkcpp/snapshot_impl.cpp b/inkcpp/snapshot_impl.cpp new file mode 100644 index 00000000..ae693367 --- /dev/null +++ b/inkcpp/snapshot_impl.cpp @@ -0,0 +1,83 @@ +#include "snapshot_impl.h" +#include "globals_impl.h" +#include "runner_impl.h" + +#include +#ifdef INK_ENABLE_STL +#include +#endif + +namespace ink::runtime +{ + snapshot* snapshot::from_binary(const unsigned char* data, size_t length, bool freeOnDestroy) + { + return new internal::snapshot_impl(data, length, freeOnDestroy); + } + +#ifdef INK_ENABLE_STL + snapshot* snapshot::from_file(const char* filename) { + std::ifstream ifs(filename, std::ios::binary | std::ios::ate); + if(!ifs.is_open()) { + throw ink_exception("Failed to open snapshot file: " + std::string(filename)); + } + + size_t length = static_cast(ifs.tellg()); + unsigned char* data = new unsigned char[length]; + ifs.seekg(0, std::ios::beg); + ifs.read(reinterpret_cast(data), length); + ifs.close(); + + return from_binary(data, length); + } +#endif +} + +namespace ink::runtime::internal +{ + size_t snapshot_impl::file_size(size_t serialization_length, size_t runner_cnt) { + return serialization_length + sizeof(header) + runner_cnt * sizeof(size_t); + } + + const unsigned char* snapshot_impl::get_data() const { + return _file; + } + size_t snapshot_impl::get_data_len() const { + return _length; + } + + snapshot_impl::snapshot_impl(const globals_impl& globals) + : _managed{true} + { + _length = globals.snap(); + size_t runner_cnt = 0; + for(auto node = globals._runners_start; node; node = node->next) + { + _length += node->object->snap(); + ++runner_cnt; + } + + _length = file_size(_length, runner_cnt); + unsigned char* data = new unsigned char[_length]; + _file = data; + unsigned char* ptr = data; + // write header + memcpy(ptr, &_header, sizeof(_header)); + // write lookup table + ptr += sizeof(header); + { + size_t offset = 0; + for(auto node = globals._runners_start; node; node = node->next) + { + offset += node->object->snap(); + memcpy(ptr, &offset, sizeof(offset)); + ptr += sizeof(offset); + } + } + + ptr += globals.snap(ptr); + for (auto node = globals._runners_start; node; node = node->next) + { + ptr += node->object->snap(ptr); + } + } +} diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h new file mode 100644 index 00000000..e65844ef --- /dev/null +++ b/inkcpp/snapshot_impl.h @@ -0,0 +1,62 @@ +#pragma once + +#include "snapshot.h" + +#include + +namespace ink::runtime::internal +{ + class globals_impl; + class value; + class string_table; + class snapshot_interface { + protected: + static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) + { + memcpy(ptr, data, length); + return ptr += length; + } + template + static unsigned char* snap_write(unsigned char* ptr, const T& data, bool write) + { + return snap_write(ptr, &data, sizeof(data), write); + } + public: + struct snapper { + const string_table& strings; + }; + struct loader { + string_table& strings; + }; + virtual size_t snap(unsigned char* data, const snapper&) const = 0; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) = 0; + }; + class snapshot_impl : public snapshot + { + public: + ~snapshot_impl() override { + if (_managed) { delete[] _file; } + }; + const unsigned char* get_data() const override; + size_t get_data_len() const override; + + snapshot_impl(const globals_impl&); + // write down all allocated strings + // replace pointer with idx + // reconsrtuct static strings index + // list_table _data & _entry_state + snapshot_impl(const unsigned char* data, size_t length, bool managed); + + private: + // file information + const unsigned char* _file; + size_t _length; + bool _managed; + static size_t file_size(size_t, size_t); + struct header { + size_t num_runners; + size_t length; + + } _header; + }; +} diff --git a/inkcpp/story_impl.h b/inkcpp/story_impl.h index bf6dea04..b832f3cc 100644 --- a/inkcpp/story_impl.h +++ b/inkcpp/story_impl.h @@ -40,7 +40,9 @@ namespace ink::runtime::internal // Creates a new global store for use with runners executing this story virtual globals new_globals() override; + virtual globals new_globals_from_snapshot(const snapshot&) override; virtual runner new_runner(globals store = nullptr) override; + virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override; const ink::internal::header& get_header() const { return _header; } diff --git a/inkcpp/string_table.cpp b/inkcpp/string_table.cpp index aa8cda88..fd113f50 100644 --- a/inkcpp/string_table.cpp +++ b/inkcpp/string_table.cpp @@ -94,4 +94,14 @@ namespace ink::runtime::internal iter++; } } + + size_t string_table::snap(unsigned char* data, const snapper&) const + { + unsigned char* ptr = data; + for(auto itr = _table.begin(); itr != _table.end(); ++itr) { + ptr = snap_write(ptr, itr.key(), strlen(itr.key()) + 1, data); + } + ptr = snap_write(ptr, "\0", 1, data); + return ptr - data; + } } diff --git a/inkcpp/string_table.h b/inkcpp/string_table.h index e2eb6049..883b5958 100644 --- a/inkcpp/string_table.h +++ b/inkcpp/string_table.h @@ -2,11 +2,12 @@ #include "avl_array.h" #include "system.h" +#include "snapshot_impl.h" namespace ink::runtime::internal { // hash tree sorted by string pointers - class string_table + class string_table : public snapshot_interface { public: ~string_table(); @@ -21,6 +22,15 @@ namespace ink::runtime::internal // mark a string as used void mark_used(const char* string); + + // snapshot interface implementation + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + + // get position of string when iterate through data + // used to enable storing a string table references + void get_id(const char* string); + // deletes all unused strings void gc(); diff --git a/inkcpp/value.h b/inkcpp/value.h index 88382f5a..e90bcb63 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -8,6 +8,7 @@ #include "system.h" #include "../shared/private/command.h" #include "list_table.h" +#include "snapshot_impl.h" #include "tuple.hpp" #ifdef INK_ENABLE_STL @@ -82,8 +83,12 @@ namespace ink::runtime::internal { /** * @brief class to wrap stack value to common type. */ - class value { + class value : public snapshot_interface { public: + // snapshot interface + size_t snap(unsigned char* data, const snapper&) const; + const unsigned char* snap_load(const unsigned char* data, const loader&); + /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; From a90088cc296b197217ead832fb10c5d9e9837e63 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 8 Jul 2022 13:52:09 +0200 Subject: [PATCH 03/14] Add snap functions --- inkcpp/array.h | 22 ++++++++++++++++- inkcpp/collections/restorable.cpp | 36 ++++++++++++++++++++++++++++ inkcpp/collections/restorable.h | 16 ++++++++++++- inkcpp/include/traits.h | 13 ++++++++++ inkcpp/runner_impl.cpp | 40 +++++++++++++++++++++++++------ inkcpp/runner_impl.h | 4 ++++ inkcpp/simple_restorable_stack.h | 24 ++++++++++++++++++- inkcpp/snapshot_impl.cpp | 16 +++++++++---- inkcpp/snapshot_impl.h | 1 + inkcpp/stack.cpp | 9 +++++++ inkcpp/stack.h | 14 ++++++++++- inkcpp/string_table.h | 2 +- inkcpp/value.cpp | 23 ++++++++++++++++++ inkcpp/value.h | 13 ++++++++++ 14 files changed, 216 insertions(+), 17 deletions(-) diff --git a/inkcpp/array.h b/inkcpp/array.h index 8b6cfddb..0eeb7b61 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -1,6 +1,7 @@ #pragma once #include "system.h" +#include "traits.h" #include "snapshot_impl.h" namespace ink::runtime::internal @@ -63,6 +64,7 @@ namespace ink::runtime::internal size_t snap(unsigned char* data, const snapper&) const override { + inkAssert(!is_pointer{}(), "here is a special case oversight"); unsigned char* ptr = data; ptr = snap_write(ptr, &_size, data); for(const T& e : *this) { @@ -99,7 +101,7 @@ namespace ink::runtime::internal } template - class basic_restorable_array + class basic_restorable_array : public snapshot_interface { public: basic_restorable_array(T* array, size_t capacity, T nullValue) @@ -136,6 +138,10 @@ namespace ink::runtime::internal // Resets all values and clears any save points void clear(const T& value); + // snapshot interface + virtual size_t snap(unsigned char* data, const snapper&) const override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + protected: inline T* buffer() { return _array; } void set_new_buffer(T* buffer, size_t capacity) { @@ -164,6 +170,20 @@ namespace ink::runtime::internal const T _null; }; + template + inline size_t basic_restorable_array::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _saved, data); + ptr = snap_write(ptr, _capacity, data); + ptr = snap_write(ptr, _null, data); + for(size_t i = 0; i < _capacity; ++i) { + ptr = snap_write(ptr, _array[i], data); + ptr = snap_write(ptr, _temp[i], data); + } + return ptr - data; + } + template inline void basic_restorable_array::set(size_t index, const T& value) { diff --git a/inkcpp/collections/restorable.cpp b/inkcpp/collections/restorable.cpp index e69de29b..34b96ed0 100644 --- a/inkcpp/collections/restorable.cpp +++ b/inkcpp/collections/restorable.cpp @@ -0,0 +1,36 @@ +#include "restorable.h" +#include "../stack.h" + +namespace ink::runtime::internal { + template<> + size_t restorable::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _pos, data); + ptr = snap_write(ptr, _jump, data); + ptr = snap_write(ptr, _save, data); + size_t max = _pos; + if (_jump > max) { max = _jump; } + if (_save > max) { max = _save; } + for(size_t i = 0; i < max; ++i) { + ptr = snap_write(ptr, _buffer[i].name, data); + ptr += _buffer[i].data.snap(data ? ptr : nullptr, snapper); + } + return ptr - data; + } + template<> + size_t restorable::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _pos, data); + ptr = snap_write(ptr, _jump, data); + ptr = snap_write(ptr, _save, data); + size_t max = _pos; + if (_jump > max) { max = _jump; } + if (_save > max) { max = _save; } + for(size_t i = 0; i < max; ++i) { + ptr += _buffer[i].snap(data ? ptr : nullptr, snapper); + } + return ptr - data; + } +} diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index ad64175e..28fa05ae 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -1,7 +1,13 @@ +#pragma once + +#include "../snapshot_impl.h" + #include +#include namespace ink::runtime::internal { + struct entry; template constexpr auto EmptyNullPredicate = [](const ElementType&) { return false; }; @@ -68,7 +74,7 @@ namespace ink::runtime::internal * from this class gain this functionality. */ template - class restorable + class restorable : public snapshot_interface { public: restorable(ElementType* buffer, size_t size) @@ -326,6 +332,10 @@ namespace ink::runtime::internal return count; } + // snapshot interface + virtual size_t snap(unsigned char* data, const snapper&) const override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + protected: // Called when we run out of space in buffer. virtual void overflow(ElementType*& buffer, size_t& size) { @@ -374,4 +384,8 @@ namespace ink::runtime::internal size_t _jump; size_t _save; }; + template<> + size_t restorable::snap(unsigned char* data, const snapper& snapper) const; + template<> + size_t restorable::snap(unsigned char* data, const snapper& snapper) const; } diff --git a/inkcpp/include/traits.h b/inkcpp/include/traits.h index 86ade898..d7733f27 100644 --- a/inkcpp/include/traits.h +++ b/inkcpp/include/traits.h @@ -9,6 +9,13 @@ namespace ink::runtime::internal { + template + constexpr size_t sizeof_largest_type() + { + size_t ret = 0; + return ( (ret = sizeof(Ts) > ret ? sizeof(Ts) : ret), ... ); + } + template struct get_ith_type : get_ith_type {}; @@ -37,6 +44,12 @@ namespace ink::runtime::internal template struct is_same : true_type {}; + template + struct is_pointer : false_type {}; + + template + struct is_pointer : true_type {}; + // == string testing (from me) == template diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 499ae741..bcde17fb 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -544,13 +544,39 @@ namespace ink::runtime::internal ptr = snap_write(ptr, _saved, data); ptr = snap_write(ptr, _is_falling, data); ptr += _output.snap(data ? ptr : nullptr, snapper); - // _output - // _stack - // _ref_stack - // _threads - // _tags - // _choices - // _container + ptr += _stack.snap(data ? ptr : nullptr, snapper); + ptr += _ref_stack.snap(data ? ptr : nullptr, snapper); + ptr += _eval.snap(data ? ptr : nullptr, snapper); + ptr = snap_write(ptr, _tags.size(), data); + for (const auto& tag : _tags) { + ptr = snap_write(ptr, tag - snapper.story_string_table, data); + } + ptr += _container.snap(data ? ptr : nullptr, snapper); + ptr += _threads.snap(data ? ptr : nullptr, snapper); + ptr = snap_write(ptr, _backup_choice_len, data); + ptr = snap_write(ptr, _fallback_choice.has_value(), data); + auto snap_choice = [&snapper, write = static_cast(data)](unsigned char* data, const choice& c) -> size_t { + unsigned char* ptr = data; + ptr = snap_write(ptr, c._index, write); + ptr = snap_write(ptr, c._path, write); + ptr = snap_write(ptr, c._thread, write); + ptr = snap_write(ptr, snapper.strings.get_id(c._text), write); + return ptr - data; + }; + if (_fallback_choice) { + ptr += snap_choice(ptr, _fallback_choice.value()); + } + for (const choice& c : _choices) { + ptr += snap_choice(ptr, c); + } + return ptr - data; + } + + size_t runner_impl::threads::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr += base::snap(data ? ptr : nullptr, snapper); + ptr += _threadDone.snap(data ? ptr : nullptr, snapper); return ptr - data; } diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 5f8eb15f..a1e64565 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -200,6 +200,10 @@ namespace ink::runtime::internal } const ip_t& operator[](size_t index) const { return get(index); } + // snapshot interface + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + protected: virtual void overflow(thread_t*& buffer, size_t& size) override final; private: diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index bf09d633..b7ccd7bf 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -2,11 +2,12 @@ #include "system.h" #include "array.h" +#include "snapshot_impl.h" namespace ink::runtime::internal { template - class simple_restorable_stack + class simple_restorable_stack : public snapshot_interface { public: simple_restorable_stack(T* buffer, size_t size, const T& null) @@ -27,6 +28,9 @@ namespace ink::runtime::internal void restore(); void forget(); + virtual size_t snap(unsigned char* data, const snapper&) const override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + protected: virtual void overflow(T*& buffer, size_t& size) { throw ink_exception("Stack overflow!"); @@ -222,4 +226,22 @@ namespace ink::runtime::internal // Just reset save position _save = InvalidIndex; } + template + size_t simple_restorable_stack::snap(unsigned char* data, const snapper&) const + { + static_assert(is_same{}() || is_same{}()); + unsigned char* ptr = data; + ptr = snap_write(ptr, _null, data); + ptr = snap_write(ptr, _pos, data); + ptr = snap_write(ptr, _save, data); + ptr = snap_write(ptr, _jump, data); + size_t max = _pos; + if (_save > max) { max = _save; } + if (_jump > max) { max = _jump; } + for(size_t i = 0; i < max; ++i) + { + ptr = snap_write(ptr, _buffer[i], data); + } + return ptr - data; + } } diff --git a/inkcpp/snapshot_impl.cpp b/inkcpp/snapshot_impl.cpp index ae693367..a5104af2 100644 --- a/inkcpp/snapshot_impl.cpp +++ b/inkcpp/snapshot_impl.cpp @@ -1,4 +1,6 @@ #include "snapshot_impl.h" + +#include "story_impl.h" #include "globals_impl.h" #include "runner_impl.h" @@ -48,11 +50,15 @@ namespace ink::runtime::internal snapshot_impl::snapshot_impl(const globals_impl& globals) : _managed{true} { - _length = globals.snap(); + snapshot_interface::snapper snapper{ + .strings = globals.strings(), + .story_string_table = globals._owner->string(0) + }; + _length = globals.snap(nullptr, snapper); size_t runner_cnt = 0; for(auto node = globals._runners_start; node; node = node->next) { - _length += node->object->snap(); + _length += node->object->snap(nullptr, snapper); ++runner_cnt; } @@ -68,16 +74,16 @@ namespace ink::runtime::internal size_t offset = 0; for(auto node = globals._runners_start; node; node = node->next) { - offset += node->object->snap(); + offset += node->object->snap(nullptr, snapper); memcpy(ptr, &offset, sizeof(offset)); ptr += sizeof(offset); } } - ptr += globals.snap(ptr); + ptr += globals.snap(ptr, snapper); for (auto node = globals._runners_start; node; node = node->next) { - ptr += node->object->snap(ptr); + ptr += node->object->snap(ptr, snapper); } } } diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index e65844ef..42debbcd 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -24,6 +24,7 @@ namespace ink::runtime::internal public: struct snapper { const string_table& strings; + const char* story_string_table; }; struct loader { string_table& strings; diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 40c73127..79dacf6c 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -587,4 +587,13 @@ namespace ink::runtime::internal stack.set(itr.get()->name, itr.get()->data); } } + + size_t basic_stack::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _next_thread, data); + ptr = snap_write(ptr, _backup_next_thread, data); + ptr += base::snap(data ? ptr : nullptr, snapper); + return ptr - data; + } } diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 147e4c54..7c3c7b35 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -3,6 +3,7 @@ #include "value.h" #include "collections/restorable.h" #include "array.h" +#include "snapshot_impl.h" namespace ink { @@ -24,7 +25,7 @@ namespace ink thread }; - class basic_stack : protected restorable + class basic_stack : protected restorable { protected: basic_stack(entry* data, size_t size); @@ -79,6 +80,10 @@ namespace ink // push all values to other _stack void push_values(basic_stack& _stack); + // snapshot interface + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + private: entry& add(hash_t name, const value& val); const entry* pop(); @@ -135,6 +140,7 @@ namespace ink using base = restorable; public: + // Push value onto the stack void push(const value&); @@ -160,6 +166,12 @@ namespace ink void save(); void restore(); void forget(); + + // snapshot interface + size_t snap(unsigned char* data, const snapper& snapper) const override + { return base::snap(data, snapper); } + const unsigned char* snap_load(const unsigned char* data, const loader& loader) override + { return base::snap_load(data, loader); } }; template diff --git a/inkcpp/string_table.h b/inkcpp/string_table.h index 883b5958..4fd0cbd2 100644 --- a/inkcpp/string_table.h +++ b/inkcpp/string_table.h @@ -29,7 +29,7 @@ namespace ink::runtime::internal // get position of string when iterate through data // used to enable storing a string table references - void get_id(const char* string); + size_t get_id(const char* string) const; // deletes all unused strings void gc(); diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index b0ff301d..e1f6b756 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -2,6 +2,7 @@ #include "output.h" #include "list_table.h" #include "string_utils.h" +#include "string_table.h" namespace ink::runtime::internal { @@ -69,4 +70,26 @@ namespace ink::runtime::internal os.append(val); return os; } + + size_t value::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _type, data); + if (_type == value_type::string) { + unsigned char buf[max_value_size]; + string_type* res = reinterpret_cast(buf); + auto str = get(); + res->allocated = str.allocated; + if (str.allocated) { + res->str = reinterpret_cast(snapper.strings.get_id(str.str)); + } else { + res->str = reinterpret_cast(str.str - snapper.story_string_table); + } + ptr = snap_write(ptr, buf, data); + } else { + // TODO more space efficent? + ptr = snap_write(ptr, &bool_value, max_value_size, data); + } + return ptr - data; + } } diff --git a/inkcpp/value.h b/inkcpp/value.h index e90bcb63..fbb9c045 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,6 +165,19 @@ namespace ink::runtime::internal { char ci; } pointer; }; + static constexpr size_t max_value_size = + sizeof_largest_type< + decltype(bool_value), + decltype(int32_value), + decltype(string_value), + decltype(uint32_value), + decltype(float_value), + decltype(jump), + decltype(list_value), + decltype(list_flag_value), + decltype(frame_value), + decltype(pointer) + >(); value_type _type; }; From d7d23a1d21d14f33cf5bff024953db7fdeb79f37 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 11 Jul 2022 15:28:47 +0200 Subject: [PATCH 04/14] Can load globals, start loading runner --- inkcpp/CMakeLists.txt | 1 + inkcpp/array.h | 25 +++++++++---- inkcpp/avl_array.h | 24 +++++++++---- inkcpp/collections/restorable.cpp | 15 ++++++++ inkcpp/collections/restorable.h | 4 ++- inkcpp/globals_impl.cpp | 13 ++++++- inkcpp/globals_impl.h | 2 +- inkcpp/include/snapshot.h | 10 +++--- inkcpp/include/story.h | 2 +- inkcpp/list_table.cpp | 7 ++++ inkcpp/output.h | 2 +- inkcpp/runner_impl.cpp | 56 +++++++++++++++++++++++++++++- inkcpp/runner_impl.h | 2 +- inkcpp/simple_restorable_stack.h | 4 +-- inkcpp/snapshot_impl.cpp | 29 ++++++++++++++-- inkcpp/snapshot_impl.h | 50 ++++++++++++-------------- inkcpp/snapshot_interface.h | 47 +++++++++++++++++++++++++ inkcpp/stack.h | 2 +- inkcpp/story_impl.cpp | 15 ++++++++ inkcpp/story_impl.h | 5 ++- inkcpp/string_table.cpp | 22 ++++++++++++ inkcpp/value.h | 4 +-- inkcpp_cl/inkcpp_cl.cpp | 26 ++++++++++---- inkcpp_compiler/binary_emitter.cpp | 6 ++-- 24 files changed, 305 insertions(+), 68 deletions(-) create mode 100644 inkcpp/snapshot_interface.h diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index 876b6b18..45e41cec 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -13,6 +13,7 @@ list(APPEND SOURCES runner_impl.h runner_impl.cpp simple_restorable_stack.h stack.h stack.cpp story_impl.h story_impl.cpp + snapshot_impl.h snapshot_impl.cpp snapshot_interface.h story_ptr.cpp system.cpp value.h value.cpp diff --git a/inkcpp/array.h b/inkcpp/array.h index 0eeb7b61..ca81910d 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -2,7 +2,7 @@ #include "system.h" #include "traits.h" -#include "snapshot_impl.h" +#include "snapshot_interface.h" namespace ink::runtime::internal { @@ -66,13 +66,26 @@ namespace ink::runtime::internal { inkAssert(!is_pointer{}(), "here is a special case oversight"); unsigned char* ptr = data; - ptr = snap_write(ptr, &_size, data); + ptr = snap_write(ptr, _size, data); for(const T& e : *this) { ptr = snap_write(ptr, e, data); } return ptr - data; } - const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* ptr, const loader&) override + { + decltype(_size) size; + ptr = snap_read(ptr, size); + if constexpr (dynamic) { + resize(size); + } else { + inkAssert(size <= initialCapacity, "capacity of non dynamic array is to small vor snapshot!"); + } + for (T& e : *this) { + ptr = snap_read(ptr, e); + } + return ptr; + } private: if_t _static_data[dynamic ? 1 : initialCapacity]; @@ -86,8 +99,8 @@ namespace ink::runtime::internal { static_assert(dynamic, "Can only extend if array is dynamic!"); size_t new_capacity = capacity > _capacity - ? 1.5f * _capacity - : capacity; + ? capacity + : 1.5f * _capacity; if (new_capacity < 5) { new_capacity = 5; } T* new_data = new T[new_capacity]; @@ -140,7 +153,7 @@ namespace ink::runtime::internal // snapshot interface virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } protected: inline T* buffer() { return _array; } diff --git a/inkcpp/avl_array.h b/inkcpp/avl_array.h index ed925706..e9c4769d 100644 --- a/inkcpp/avl_array.h +++ b/inkcpp/avl_array.h @@ -87,12 +87,19 @@ class avl_array friend avl_array; // avl_array may access index pointer public: + // ctor tag_avl_array_iterator(if_t instance = nullptr, size_type idx = 0U) : instance_(instance) , idx_(idx) { } + template> + tag_avl_array_iterator(const tag_avl_array_iterator& itr) + : instance_(itr.instance_) + , idx_(itr.idx_) + {} + inline tag_avl_array_iterator& operator=(const tag_avl_array_iterator& other) { instance_ = other.instance_; @@ -118,6 +125,11 @@ class avl_array inline const Key& key() const { return instance_->key_[idx_]; } + // returns unique number for each entry + // the numbers are unique as long no operation are executed + // on the avl + inline size_t temp_identifier() const { return idx_; } + // preincrement tag_avl_array_iterator& operator++() { @@ -193,12 +205,7 @@ class avl_array inline const_iterator begin() const { - size_type i = INVALID_IDX; - if (root_ != INVALID_IDX) { - // find smallest element, it's the farthest node left from root - for (i = root_; child_[i].left != INVALID_IDX; i = child_[i].left); - } - return const_iterator(this, i); + return const_iterator(const_cast(*this).begin()); } inline iterator end() @@ -341,6 +348,11 @@ class avl_array return end(); } + inline const_iterator find(const key_type& key) const + { + return const_iterator(const_cast(*this).find(key)); + } + /** * Count elements with a specific key diff --git a/inkcpp/collections/restorable.cpp b/inkcpp/collections/restorable.cpp index 34b96ed0..961d21b5 100644 --- a/inkcpp/collections/restorable.cpp +++ b/inkcpp/collections/restorable.cpp @@ -33,4 +33,19 @@ namespace ink::runtime::internal { } return ptr - data; } + template<> + size_t restorable::snap(unsigned char* data, const snapper&) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _pos, data); + ptr = snap_write(ptr, _jump, data); + ptr = snap_write(ptr, _save, data); + size_t max = _pos; + if (_jump > max) { max = _jump; } + if (_save > max) { max = _save; } + for(size_t i = 0; i < max; ++i) { + ptr = snap_write(ptr, _buffer[i], data); + } + return ptr - data; + } } diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index 28fa05ae..0ddb8a77 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -334,7 +334,7 @@ namespace ink::runtime::internal // snapshot interface virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } protected: // Called when we run out of space in buffer. @@ -388,4 +388,6 @@ namespace ink::runtime::internal size_t restorable::snap(unsigned char* data, const snapper& snapper) const; template<> size_t restorable::snap(unsigned char* data, const snapper& snapper) const; + template<> + size_t restorable::snap(unsigned char* data, const snapper&) const; } diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 90e5cb90..c1544b24 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -184,6 +184,7 @@ namespace ink::runtime::internal // execute one line to startup the globals run->getline_silent(); + _globals_initialized = true; } void globals_impl::gc() @@ -228,7 +229,7 @@ namespace ink::runtime::internal size_t globals_impl::snap(unsigned char* data, const snapper& snapper) const { - unsigned char* ptr; + unsigned char* ptr = data; inkAssert(_num_containers == _visit_counts.size(), "Should be equal!"); inkAssert(_globals_initialized, "Only support snapshot of globals with runner! or you don't need a snapshot for this state"); ptr += _visit_counts.snap( data ? ptr : nullptr, snapper ); @@ -236,4 +237,14 @@ namespace ink::runtime::internal ptr += _lists.snap( data ? ptr : nullptr, snapper ); return ptr - data; } + + const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loader& loader) + { + _globals_initialized = true; + ptr = _visit_counts.snap_load(ptr, loader); + inkAssert(_num_containers == _visit_counts.size(), "errer when loading visit counts, story file dont match snapshot!"); + ptr = _strings.snap_load(ptr, loader); + ptr = _lists.snap_load(ptr, loader); + return ptr; + } } diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index 70ab71ae..ea51a3d7 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -18,9 +18,9 @@ namespace ink::runtime::internal class globals_impl : public globals_interface, public snapshot_interface { friend snapshot_impl; + public: size_t snap(unsigned char* data, const snapper&) const override; const unsigned char* snap_load(const unsigned char* data, const loader&) override; - public: // Initializes a new global store from the given story globals_impl(const story_impl*); virtual ~globals_impl() { } diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h index 8d89c687..ad4de434 100644 --- a/inkcpp/include/snapshot.h +++ b/inkcpp/include/snapshot.h @@ -6,14 +6,16 @@ namespace ink::runtime { class snapshot { public: - virtual ~snapshot() = 0; + virtual ~snapshot() {}; static snapshot* from_binary(const unsigned char* data, size_t length, bool freeOnDestroy = true); -#ifdef INK_ENABLE_STL - static snapshot* from_file(const char* filename); -#endif virtual const unsigned char* get_data() const = 0; virtual size_t get_data_len() const = 0; + +#ifdef INK_ENABLE_STL + static snapshot* from_file(const char* filename); + void write_to_file(const char* filename) const; +#endif }; } diff --git a/inkcpp/include/story.h b/inkcpp/include/story.h index 9f7e4935..c62cbbdd 100644 --- a/inkcpp/include/story.h +++ b/inkcpp/include/story.h @@ -19,7 +19,7 @@ namespace ink::runtime class story { public: - virtual ~story() = 0; + virtual ~story(){}; #pragma region Interface Methods /** * Creates a new global store diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index cf8a9cb5..21cc5093 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -658,5 +658,12 @@ namespace ink::runtime::internal return ptr - data; } + const unsigned char* list_table::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = _data.snap_load(ptr, loader); + ptr = _entry_state.snap_load(ptr, loader); + return ptr; + } + } diff --git a/inkcpp/output.h b/inkcpp/output.h index 1185ee20..e4a07eaa 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -102,7 +102,7 @@ namespace ink // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } private: size_t find_start() const; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index bcde17fb..7a509603 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -549,7 +549,8 @@ namespace ink::runtime::internal ptr += _eval.snap(data ? ptr : nullptr, snapper); ptr = snap_write(ptr, _tags.size(), data); for (const auto& tag : _tags) { - ptr = snap_write(ptr, tag - snapper.story_string_table, data); + std::uintptr_t offset = tag - snapper.story_string_table; + ptr = snap_write(ptr, offset, data); } ptr += _container.snap(data ? ptr : nullptr, snapper); ptr += _threads.snap(data ? ptr : nullptr, snapper); @@ -566,12 +567,65 @@ namespace ink::runtime::internal if (_fallback_choice) { ptr += snap_choice(ptr, _fallback_choice.value()); } + ptr = snap_write(ptr, _choices.size(), data); for (const choice& c : _choices) { ptr += snap_choice(ptr, c); } return ptr - data; } + const unsigned char* runner_impl::snap_load(const unsigned char* data, const loader& loader) + { + auto ptr = data; + ptr = snap_read(ptr, _ptr); + ptr = snap_read(ptr, _backup); + ptr = snap_read(ptr, _done); + int32_t seed; + ptr = snap_read(ptr, seed); + _rng.srand(seed); + ptr = snap_read(ptr, _evaluation_mode); + ptr = snap_read(ptr, _saved_evaluation_mode); + ptr = snap_read(ptr, _eval); + ptr = snap_read(ptr, _saved); + ptr = snap_read(ptr, _is_falling); + ptr = _output.snap_load(ptr, loader); + ptr = _stack.snap_load(ptr, loader); + ptr = _ref_stack.snap_load(ptr, loader); + ptr = _eval.snap_load(ptr, loader); + size_t num_tags; + ptr = snap_read(ptr, num_tags); + for(size_t i = 0; i < num_tags; ++i) { + std::uintptr_t offset; + ptr = snap_read(ptr, offset); + _tags.push() = offset + loader.story_string_table; + } + ptr = _container.snap_load(ptr, loader); + ptr = _threads.snap_load(ptr, loader); + ptr = snap_read(ptr, _backup_choice_len); + auto read_choice = [&ptr,&loader]() -> choice{ + choice c; + ptr = snap_read(ptr, c._index); + ptr = snap_read(ptr, c._path); + ptr = snap_read(ptr, c._thread); + size_t string_id; + ptr = snap_read(ptr, string_id); + c._text = loader.string_table[string_id]; + return c; + }; + bool has_fallback_choice; + ptr = snap_read(ptr, has_fallback_choice); + _fallback_choice = has_fallback_choice + ? optional{read_choice()} + : nullopt; + size_t num_choices; + ptr = snap_read(ptr, num_choices); + for (size_t i = 0; i < num_choices; ++i) { + _choices.push() = read_choice(); + } + + return ptr; + } + size_t runner_impl::threads::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index a1e64565..5de6f2c2 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -202,7 +202,7 @@ namespace ink::runtime::internal // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } protected: virtual void overflow(thread_t*& buffer, size_t& size) override final; diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index b7ccd7bf..1616e2a6 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -29,7 +29,7 @@ namespace ink::runtime::internal void forget(); virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } protected: virtual void overflow(T*& buffer, size_t& size) { @@ -229,7 +229,7 @@ namespace ink::runtime::internal template size_t simple_restorable_stack::snap(unsigned char* data, const snapper&) const { - static_assert(is_same{}() || is_same{}()); + static_assert(is_same{}() || is_same{}() || is_same{}()); unsigned char* ptr = data; ptr = snap_write(ptr, _null, data); ptr = snap_write(ptr, _pos, data); diff --git a/inkcpp/snapshot_impl.cpp b/inkcpp/snapshot_impl.cpp index a5104af2..28c6c179 100644 --- a/inkcpp/snapshot_impl.cpp +++ b/inkcpp/snapshot_impl.cpp @@ -31,13 +31,23 @@ namespace ink::runtime return from_binary(data, length); } + + void snapshot::write_to_file(const char* filename) const + { + std::ofstream ofs(filename, std::ios::binary); + if(!ofs.is_open()) { + throw ink_exception("Failed to open file to write snapshot: " + + std::string(filename)); + } + ofs.write(reinterpret_cast(get_data()), get_data_len()); + } #endif } namespace ink::runtime::internal { size_t snapshot_impl::file_size(size_t serialization_length, size_t runner_cnt) { - return serialization_length + sizeof(header) + runner_cnt * sizeof(size_t); + return serialization_length + sizeof(header) + (runner_cnt + 1) * sizeof(size_t); } const unsigned char* snapshot_impl::get_data() const { @@ -63,6 +73,8 @@ namespace ink::runtime::internal } _length = file_size(_length, runner_cnt); + _header.length = _length; + _header.num_runners = runner_cnt; unsigned char* data = new unsigned char[_length]; _file = data; unsigned char* ptr = data; @@ -71,12 +83,15 @@ namespace ink::runtime::internal // write lookup table ptr += sizeof(header); { - size_t offset = 0; + size_t offset = (ptr - data) + (_header.num_runners + 1) * sizeof(size_t); + memcpy(ptr, &offset, sizeof(offset)); + ptr += sizeof(offset); + offset += globals.snap(nullptr, snapper); for(auto node = globals._runners_start; node; node = node->next) { - offset += node->object->snap(nullptr, snapper); memcpy(ptr, &offset, sizeof(offset)); ptr += sizeof(offset); + offset += node->object->snap(nullptr, snapper); } } @@ -86,4 +101,12 @@ namespace ink::runtime::internal ptr += node->object->snap(ptr, snapper); } } + + snapshot_impl::snapshot_impl(const unsigned char* data, size_t length, bool managed) + : _file{data}, _length{length}, _managed{managed} + { + const unsigned char* ptr = data; + memcpy(&_header, ptr, sizeof(_header)); + inkAssert(_header.length == _length, "Corrupted file length"); + } } diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index 42debbcd..e90cb480 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -1,43 +1,23 @@ #pragma once #include "snapshot.h" +#include "snapshot_interface.h" +#include "array.h" -#include namespace ink::runtime::internal { - class globals_impl; - class value; - class string_table; - class snapshot_interface { - protected: - static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) - { - memcpy(ptr, data, length); - return ptr += length; - } - template - static unsigned char* snap_write(unsigned char* ptr, const T& data, bool write) - { - return snap_write(ptr, &data, sizeof(data), write); - } - public: - struct snapper { - const string_table& strings; - const char* story_string_table; - }; - struct loader { - string_table& strings; - }; - virtual size_t snap(unsigned char* data, const snapper&) const = 0; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) = 0; - }; + class snapshot_impl : public snapshot { public: ~snapshot_impl() override { if (_managed) { delete[] _file; } }; + + managed_array& strings() const { + return string_table; + } const unsigned char* get_data() const override; size_t get_data_len() const override; @@ -48,8 +28,18 @@ namespace ink::runtime::internal // list_table _data & _entry_state snapshot_impl(const unsigned char* data, size_t length, bool managed); + const unsigned char* get_globals_snap() const { + return _file + get_offset(0); + } + + const unsigned char* get_runner_snap(size_t idx) const { + return _file + get_offset(idx + 1); + } + private: // file information + // only populated when loading snapshots + mutable managed_array string_table; const unsigned char* _file; size_t _length; bool _managed; @@ -59,5 +49,11 @@ namespace ink::runtime::internal size_t length; } _header; + + size_t get_offset(size_t idx) const { + inkAssert(idx <= _header.num_runners); + return reinterpret_cast(_file + sizeof(header))[idx]; + } + }; } diff --git a/inkcpp/snapshot_interface.h b/inkcpp/snapshot_interface.h new file mode 100644 index 00000000..7609991a --- /dev/null +++ b/inkcpp/snapshot_interface.h @@ -0,0 +1,47 @@ +#pragma once + +#include "snapshot.h" +#include + +namespace ink::runtime::internal +{ + class globals_impl; + class value; + class string_table; + template + class managed_array; + class snapshot_interface { + protected: + static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) + { + if (write) { memcpy(ptr, data, length); } + return ptr + length; + } + template + static unsigned char* snap_write(unsigned char* ptr, const T& data, bool write) + { + return snap_write(ptr, &data, sizeof(data), write); + } + static const unsigned char* snap_read(const unsigned char* ptr, void* data, size_t length) + { + memcpy(data, ptr, length); + return ptr + length; + } + template + static const unsigned char* snap_read(const unsigned char* ptr, T& data) + { + return snap_read(ptr, &data, sizeof(data)); + } + public: + struct snapper { + const string_table& strings; + const char* story_string_table; + }; + struct loader { + managed_array& string_table; + const char* story_string_table; + }; + virtual size_t snap(unsigned char* data, const snapper&) const = 0; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) = 0; + }; +} diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 7c3c7b35..8ef1764d 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -82,7 +82,7 @@ namespace ink // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } private: entry& add(hash_t name, const value& val); diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 8f96c54b..0cc1a444 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -2,6 +2,8 @@ #include "platform.h" #include "runner_impl.h" #include "globals_impl.h" +#include "snapshot.h" +#include "snapshot_impl.h" #include "version.h" #ifdef INK_ENABLE_STL @@ -175,6 +177,19 @@ namespace ink::runtime::internal return globals(new globals_impl(this), _block); } + globals story_impl::new_globals_from_snapshot(const snapshot& data) + { + const snapshot_impl& snapshot = reinterpret_cast(data); + auto* globs = new globals_impl(this); + auto end = globs->snap_load(snapshot.get_globals_snap(), snapshot_interface::loader{ + .string_table = snapshot.strings(), + .story_string_table = _string_table, + + }); + inkAssert(end == snapshot.get_runner_snap(0), "not all data were used for global reconstruction"); + return globals(globs, _block); + } + runner story_impl::new_runner(globals store) { if (store == nullptr) diff --git a/inkcpp/story_impl.h b/inkcpp/story_impl.h index b832f3cc..ef73264f 100644 --- a/inkcpp/story_impl.h +++ b/inkcpp/story_impl.h @@ -42,7 +42,10 @@ namespace ink::runtime::internal virtual globals new_globals() override; virtual globals new_globals_from_snapshot(const snapshot&) override; virtual runner new_runner(globals store = nullptr) override; - virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override; + virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override { + inkAssert(false, "not implmented yet!"); + return nullptr; + } const ink::internal::header& get_header() const { return _header; } diff --git a/inkcpp/string_table.cpp b/inkcpp/string_table.cpp index fd113f50..3647c68d 100644 --- a/inkcpp/string_table.cpp +++ b/inkcpp/string_table.cpp @@ -104,4 +104,26 @@ namespace ink::runtime::internal ptr = snap_write(ptr, "\0", 1, data); return ptr - data; } + + const unsigned char* string_table::snap_load(const unsigned char* data, const loader& loader) + { + auto* ptr = data; + while(*ptr) { + size_t len = 0; + for(;ptr[len];++len); + ++len; + auto str = create(len); + loader.string_table.push() = str; + ptr = snap_read(ptr, str, len); + mark_used(str); + } + return ptr + 1; + } + + size_t string_table::get_id(const char* string) const + { + auto iter = _table.find(string); + inkAssert(iter != _table.end(), "Try to fetch not contained string!"); + return iter.temp_identifier(); + } } diff --git a/inkcpp/value.h b/inkcpp/value.h index fbb9c045..c450b903 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -86,8 +86,8 @@ namespace ink::runtime::internal { class value : public snapshot_interface { public: // snapshot interface - size_t snap(unsigned char* data, const snapper&) const; - const unsigned char* snap_load(const unsigned char* data, const loader&); + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 27259dce..92047431 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "test.h" @@ -19,7 +20,7 @@ void usage() cout << "Usage: inkcpp_cl \n" << "\t-o :\tOutput file name\n" - << "\t-p:\tPlay mode\n" + << "\t-p []:\tPlay mode\n\toptional snapshot file to load\n" << endl; } @@ -35,6 +36,7 @@ int main(int argc, const char** argv) // Parse options std::string outputFilename; bool playMode = false, testMode = false, testDirectory = false; + std::string snapshotFile; for (int i = 1; i < argc - 1; i++) { std::string option = argv[i]; @@ -43,8 +45,13 @@ int main(int argc, const char** argv) outputFilename = argv[i + 1]; i += 1; } - else if (option == "-p") + else if (option == "-p") { playMode = true; + if (i + 1 < argc - 1 && argv[i+1][0] != '-') { + ++i; + snapshotFile = argv[i]; + } + } else if (option == "-t") testMode = true; else if (option == "-td") @@ -139,7 +146,11 @@ int main(int argc, const char** argv) story* myInk = story::from_file(outputFilename.c_str()); // Start runner - runner thread = myInk->new_runner(); + runner thread; + if (snapshotFile.size()) { + globals glob = myInk->new_globals_from_snapshot(*snapshot::from_file(snapshotFile.c_str())); + } + thread = myInk->new_runner(); while (true) { @@ -167,18 +178,21 @@ int main(int argc, const char** argv) int c = 0; std::cin >> c; + if (c == -1) { + snapshot* snap = thread->create_snapshot(); + // snap->write_to_file("test.snap"); + snap->write_to_file("test.snap"); + break; + } thread->choose(c - 1); std::cout << "?> "; continue; - continue; } // out of content break; } - delete myInk; - return 0; } catch (const std::exception& e) diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index dd163e63..defe4383 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -67,9 +67,9 @@ namespace ink::compiler::internal // Clear lists children.clear(); - named_children.clear(); - indexed_children.clear(); - noop_offsets.clear(); + //named_children.clear(); + //indexed_children.clear(); + //noop_offsets.clear(); parent = nullptr; } }; From 460a81b20e4fddbf8c0d81fbb1e0567f27d98a6f Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 11 Jul 2022 23:51:30 +0200 Subject: [PATCH 05/14] can store and load --- inkcpp/array.h | 18 ++++++- inkcpp/avl_array.h | 2 +- inkcpp/collections/restorable.cpp | 81 ++++++++++++++++++++++++------- inkcpp/collections/restorable.h | 9 +++- inkcpp/globals_impl.cpp | 2 + inkcpp/output.cpp | 14 +++++- inkcpp/output.h | 2 +- inkcpp/runner_impl.cpp | 8 ++- inkcpp/runner_impl.h | 2 +- inkcpp/simple_restorable_stack.h | 23 ++++++++- inkcpp/snapshot_impl.h | 2 + inkcpp/snapshot_interface.h | 4 +- inkcpp/stack.cpp | 8 +++ inkcpp/stack.h | 2 +- inkcpp/story_impl.cpp | 18 +++++++ inkcpp/story_impl.h | 6 +-- inkcpp/string_table.cpp | 10 +++- inkcpp/value.cpp | 13 +++++ inkcpp/value.h | 2 +- inkcpp_cl/inkcpp_cl.cpp | 5 +- 20 files changed, 191 insertions(+), 40 deletions(-) diff --git a/inkcpp/array.h b/inkcpp/array.h index ca81910d..c72ca2e6 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -153,7 +153,7 @@ namespace ink::runtime::internal // snapshot interface virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; protected: inline T* buffer() { return _array; } @@ -197,6 +197,22 @@ namespace ink::runtime::internal return ptr - data; } + template + inline const unsigned char* basic_restorable_array::snap_load(const unsigned char* data, const loader& loader) + { + auto ptr = data; + ptr = snap_read(ptr, _saved); + ptr = snap_read(ptr, _capacity); + T null; + ptr = snap_read(ptr, null); + inkAssert(null == _null, "null value is different to snapshot!"); + for(size_t i = 0; i < _capacity; ++i) { + ptr = snap_read(ptr, _array[i]); + ptr = snap_read(ptr, _temp[i]); + } + return ptr; + } + template inline void basic_restorable_array::set(size_t index, const T& value) { diff --git a/inkcpp/avl_array.h b/inkcpp/avl_array.h index e9c4769d..54db8b9e 100644 --- a/inkcpp/avl_array.h +++ b/inkcpp/avl_array.h @@ -128,7 +128,7 @@ class avl_array // returns unique number for each entry // the numbers are unique as long no operation are executed // on the avl - inline size_t temp_identifier() const { return idx_; } + inline size_t temp_identifier() const { return instance_->size() - idx_ - 1; } // preincrement tag_avl_array_iterator& operator++() diff --git a/inkcpp/collections/restorable.cpp b/inkcpp/collections/restorable.cpp index 961d21b5..e9b4b43d 100644 --- a/inkcpp/collections/restorable.cpp +++ b/inkcpp/collections/restorable.cpp @@ -2,16 +2,33 @@ #include "../stack.h" namespace ink::runtime::internal { + unsigned char* snap_base(unsigned char* ptr, bool write, size_t pos, size_t jump, size_t save, size_t& max) + { + ptr = snapshot_interface::snap_write(ptr, pos, write); + ptr = snapshot_interface::snap_write(ptr, jump, write); + ptr = snapshot_interface::snap_write(ptr, save, write); + max = pos; + if (jump > max) { max = jump; } + if (save > max) { max = save; } + return ptr; + } + const unsigned char* snap_load_base(const unsigned char* ptr, size_t& pos, size_t& jump, size_t& save, size_t& max) + { + ptr = snapshot_interface::snap_read(ptr, pos); + ptr = snapshot_interface::snap_read(ptr, jump); + ptr = snapshot_interface::snap_read(ptr, save); + max = pos; + if (jump > max) { max = jump; } + if (save > max) { max = save; } + return ptr; + } + template<> size_t restorable::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _pos, data); - ptr = snap_write(ptr, _jump, data); - ptr = snap_write(ptr, _save, data); - size_t max = _pos; - if (_jump > max) { max = _jump; } - if (_save > max) { max = _save; } + size_t max; + ptr = snap_base(ptr, data, _pos, _jump, _save, max); for(size_t i = 0; i < max; ++i) { ptr = snap_write(ptr, _buffer[i].name, data); ptr += _buffer[i].data.snap(data ? ptr : nullptr, snapper); @@ -22,12 +39,8 @@ namespace ink::runtime::internal { size_t restorable::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _pos, data); - ptr = snap_write(ptr, _jump, data); - ptr = snap_write(ptr, _save, data); - size_t max = _pos; - if (_jump > max) { max = _jump; } - if (_save > max) { max = _save; } + size_t max; + ptr = snap_base(ptr, data, _pos, _jump, _save, max); for(size_t i = 0; i < max; ++i) { ptr += _buffer[i].snap(data ? ptr : nullptr, snapper); } @@ -37,15 +50,47 @@ namespace ink::runtime::internal { size_t restorable::snap(unsigned char* data, const snapper&) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _pos, data); - ptr = snap_write(ptr, _jump, data); - ptr = snap_write(ptr, _save, data); - size_t max = _pos; - if (_jump > max) { max = _jump; } - if (_save > max) { max = _save; } + size_t max; + ptr = snap_base(ptr, data, _pos, _jump, _save, max); for(size_t i = 0; i < max; ++i) { ptr = snap_write(ptr, _buffer[i], data); } return ptr - data; } + + template<> + const unsigned char* restorable::snap_load(const unsigned char* ptr, const loader& loader) + { + size_t max; + ptr = snap_load_base(ptr, _pos, _jump, _save, max); + while(_size < max) { overflow(_buffer, _size); } + for(size_t i = 0; i < max; ++i) { + ptr = snap_read(ptr, _buffer[i].name); + ptr = _buffer[i].data.snap_load(ptr, loader); + } + return ptr; + } + template<> + const unsigned char* restorable::snap_load(const unsigned char* ptr, const loader& loader) + { + size_t max; + ptr = snap_load_base(ptr, _pos, _jump, _save, max); + while(_size < max) { overflow(_buffer, _size); } + for(size_t i = 0; i < max; ++i) { + ptr = _buffer[i].snap_load(ptr, loader); + } + return ptr; + } + template<> + const unsigned char* restorable::snap_load(const unsigned char* ptr, const loader& loader) + { + size_t max; + ptr = snap_load_base(ptr, _pos, _jump, _save, max); + while(_size < max) { overflow(_buffer, _size); } + for(size_t i = 0; i < max; ++i) { + ptr = snap_read(ptr, _buffer[i]); + } + return ptr; + } + } diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index 0ddb8a77..7656fe91 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -334,7 +334,7 @@ namespace ink::runtime::internal // snapshot interface virtual size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; protected: // Called when we run out of space in buffer. @@ -390,4 +390,11 @@ namespace ink::runtime::internal size_t restorable::snap(unsigned char* data, const snapper& snapper) const; template<> size_t restorable::snap(unsigned char* data, const snapper&) const; + + template<> + const unsigned char* restorable::snap_load(const unsigned char* data, const loader&); + template<> + const unsigned char* restorable::snap_load(const unsigned char* data, const loader&); + template<> + const unsigned char* restorable::snap_load(const unsigned char* data, const loader&); } diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index c1544b24..e54f3587 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -235,6 +235,7 @@ namespace ink::runtime::internal ptr += _visit_counts.snap( data ? ptr : nullptr, snapper ); ptr += _strings.snap( data ? ptr : nullptr, snapper ); ptr += _lists.snap( data ? ptr : nullptr, snapper ); + ptr += _variables.snap(data ? ptr : nullptr, snapper ); return ptr - data; } @@ -245,6 +246,7 @@ namespace ink::runtime::internal inkAssert(_num_containers == _visit_counts.size(), "errer when loading visit counts, story file dont match snapshot!"); ptr = _strings.snap_load(ptr, loader); ptr = _lists.snap_load(ptr, loader); + ptr = _variables.snap_load(ptr, loader); return ptr; } } diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 976faa54..8a76082f 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -499,7 +499,6 @@ namespace ink::runtime::internal size_t basic_stream::snap(unsigned char* data, const snapper& snapper) const { - const string_table& strings = snapper.strings; unsigned char* ptr = data; ptr = snap_write(ptr, _last_char, data); ptr = snap_write(ptr, _size, data); @@ -510,4 +509,17 @@ namespace ink::runtime::internal } return ptr - data; } + + const unsigned char* basic_stream::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = snap_read(ptr, _last_char); + ptr = snap_read(ptr, _size); + ptr = snap_read(ptr, _save); + inkAssert(_max >= _size, "output is to small to hold stored data"); + for(auto itr = _data; itr != _data + _size; ++itr) + { + ptr = itr->snap_load(ptr, loader); + } + return ptr; + } } diff --git a/inkcpp/output.h b/inkcpp/output.h index e4a07eaa..1185ee20 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -102,7 +102,7 @@ namespace ink // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; private: size_t find_start() const; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 7a509603..7e0bda49 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -540,7 +540,6 @@ namespace ink::runtime::internal ptr = snap_write(ptr, _rng.get_state(), data); ptr = snap_write(ptr, _evaluation_mode, data); ptr = snap_write(ptr, _saved_evaluation_mode, data); - ptr = snap_write(ptr, _eval, data); ptr = snap_write(ptr, _saved, data); ptr = snap_write(ptr, _is_falling, data); ptr += _output.snap(data ? ptr : nullptr, snapper); @@ -585,7 +584,6 @@ namespace ink::runtime::internal _rng.srand(seed); ptr = snap_read(ptr, _evaluation_mode); ptr = snap_read(ptr, _saved_evaluation_mode); - ptr = snap_read(ptr, _eval); ptr = snap_read(ptr, _saved); ptr = snap_read(ptr, _is_falling); ptr = _output.snap_load(ptr, loader); @@ -633,6 +631,12 @@ namespace ink::runtime::internal ptr += _threadDone.snap(data ? ptr : nullptr, snapper); return ptr - data; } + const unsigned char* runner_impl::threads::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = base::snap_load(ptr, loader); + ptr = _threadDone.snap_load(ptr, loader); + return ptr; + } #ifdef INK_ENABLE_CSTD char* runner_impl::getline_alloc() diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 5de6f2c2..a1e64565 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -202,7 +202,7 @@ namespace ink::runtime::internal // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; protected: virtual void overflow(thread_t*& buffer, size_t& size) override final; diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index 1616e2a6..1d1a4a59 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -29,7 +29,7 @@ namespace ink::runtime::internal void forget(); virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; protected: virtual void overflow(T*& buffer, size_t& size) { @@ -244,4 +244,25 @@ namespace ink::runtime::internal } return ptr - data; } + + template + const unsigned char* simple_restorable_stack::snap_load(const unsigned char* ptr, const loader& loader) + { + static_assert(is_same{}() || is_same{}() || is_same{}()); + T null; + ptr = snap_read(ptr, null); + inkAssert(null == _null, "different null value compared to snapshot!"); + ptr = snap_read(ptr, _pos); + ptr = snap_read(ptr, _save); + ptr = snap_read(ptr, _jump); + size_t max = _pos; + if(_save > max) { max = _save; } + if(_jump > max) { max = _jump; } + while(_size < max) { overflow(_buffer, _size); } + for(size_t i = 0; i < max; ++i) + { + ptr = snap_read(ptr, _buffer[i]); + } + return ptr; + } } diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index e90cb480..ea13f002 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -36,6 +36,8 @@ namespace ink::runtime::internal return _file + get_offset(idx + 1); } + size_t num_runners() const { return _header.num_runners; } + private: // file information // only populated when loading snapshots diff --git a/inkcpp/snapshot_interface.h b/inkcpp/snapshot_interface.h index 7609991a..12c6436f 100644 --- a/inkcpp/snapshot_interface.h +++ b/inkcpp/snapshot_interface.h @@ -11,7 +11,7 @@ namespace ink::runtime::internal template class managed_array; class snapshot_interface { - protected: + public: static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) { if (write) { memcpy(ptr, data, length); } @@ -32,7 +32,7 @@ namespace ink::runtime::internal { return snap_read(ptr, &data, sizeof(data)); } - public: + struct snapper { const string_table& strings; const char* story_string_table; diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 79dacf6c..bf722381 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -596,4 +596,12 @@ namespace ink::runtime::internal ptr += base::snap(data ? ptr : nullptr, snapper); return ptr - data; } + + const unsigned char* basic_stack::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = snap_read(ptr, _next_thread); + ptr = snap_read(ptr, _backup_next_thread); + ptr = base::snap_load(ptr, loader); + return ptr; + } } diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 8ef1764d..7c3c7b35 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -82,7 +82,7 @@ namespace ink // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; private: entry& add(hash_t name, const value& val); diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 0cc1a444..147903bb 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -197,6 +197,24 @@ namespace ink::runtime::internal return runner(new runner_impl(this, store), _block); } + runner story_impl::new_runner_from_snapshot(const snapshot& data, globals store, unsigned idx) + { + const snapshot_impl& snapshot = reinterpret_cast(data); + if (store == nullptr) + store = new_globals_from_snapshot(snapshot); + auto* run = new runner_impl(this, store); + auto end = run->snap_load(snapshot.get_runner_snap(idx), + snapshot_interface::loader{ + .string_table = snapshot.strings(), + .story_string_table = _string_table, + }); + inkAssert( + (idx + 1 < snapshot.num_runners() && end == snapshot.get_runner_snap(idx + 1)) + || end == snapshot.get_data() + snapshot.get_data_len(), "not all data were used for runner reconstruction" + ); + return runner(run, _block); + } + void story_impl::setup_pointers() { using header = ink::internal::header; diff --git a/inkcpp/story_impl.h b/inkcpp/story_impl.h index ef73264f..2bfa322e 100644 --- a/inkcpp/story_impl.h +++ b/inkcpp/story_impl.h @@ -42,11 +42,7 @@ namespace ink::runtime::internal virtual globals new_globals() override; virtual globals new_globals_from_snapshot(const snapshot&) override; virtual runner new_runner(globals store = nullptr) override; - virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override { - inkAssert(false, "not implmented yet!"); - return nullptr; - } - + virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override; const ink::internal::header& get_header() const { return _header; } private: diff --git a/inkcpp/string_table.cpp b/inkcpp/string_table.cpp index 3647c68d..a34459df 100644 --- a/inkcpp/string_table.cpp +++ b/inkcpp/string_table.cpp @@ -98,8 +98,13 @@ namespace ink::runtime::internal size_t string_table::snap(unsigned char* data, const snapper&) const { unsigned char* ptr = data; - for(auto itr = _table.begin(); itr != _table.end(); ++itr) { - ptr = snap_write(ptr, itr.key(), strlen(itr.key()) + 1, data); + for (size_t i = 0; i < _table.size(); ++i) { + for(auto itr = _table.begin(); itr != _table.end(); ++itr) { + if (itr.temp_identifier() == i) { + ptr = snap_write(ptr, itr.key(), strlen(itr.key()) + 1, data); + break; + } + } } ptr = snap_write(ptr, "\0", 1, data); return ptr - data; @@ -108,6 +113,7 @@ namespace ink::runtime::internal const unsigned char* string_table::snap_load(const unsigned char* data, const loader& loader) { auto* ptr = data; + int i = 0; while(*ptr) { size_t len = 0; for(;ptr[len];++len); diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index e1f6b756..38bd7ffb 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -92,4 +92,17 @@ namespace ink::runtime::internal } return ptr - data; } + const unsigned char* value::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = snap_read(ptr, _type); + ptr = snap_read(ptr, &bool_value, max_value_size); + if(_type == value_type::string) { + if(string_value.allocated) { + string_value.str = loader.string_table[(std::uintptr_t)(string_value.str)]; + } else { + string_value.str = loader.story_string_table +(std::uintptr_t)(string_value.str); + } + } + return ptr; + } } diff --git a/inkcpp/value.h b/inkcpp/value.h index c450b903..3415b55c 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -87,7 +87,7 @@ namespace ink::runtime::internal { public: // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 92047431..1f87196e 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -148,9 +148,10 @@ int main(int argc, const char** argv) // Start runner runner thread; if (snapshotFile.size()) { - globals glob = myInk->new_globals_from_snapshot(*snapshot::from_file(snapshotFile.c_str())); + thread = myInk->new_runner_from_snapshot(*snapshot::from_file(snapshotFile.c_str())); + } else { + thread = myInk->new_runner(); } - thread = myInk->new_runner(); while (true) { From 0d659818d1f4331474b7c95fea13709234565b28 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 12 Jul 2022 00:06:30 +0200 Subject: [PATCH 06/14] Add some docu --- README.md | 2 -- inkcpp/include/snapshot.h | 1 + inkcpp/snapshot_impl.h | 2 +- inkcpp_cl/inkcpp_cl.cpp | 5 ++--- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 35195eaa..7fb9cffe 100644 --- a/README.md +++ b/README.md @@ -136,8 +136,6 @@ The big things we're missing right now are: * Fallback functions for externals. * Variable observers -* Lists and whatever cool, crazy stuff Ink has been adding recently. -* Robust tests using ink-proof. ## Dependencies The compiler depends on Nlohmann's JSON library and the C++ STL. diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h index ad4de434..987a7564 100644 --- a/inkcpp/include/snapshot.h +++ b/inkcpp/include/snapshot.h @@ -12,6 +12,7 @@ namespace ink::runtime virtual const unsigned char* get_data() const = 0; virtual size_t get_data_len() const = 0; + virtual size_t num_runners() const = 0; #ifdef INK_ENABLE_STL static snapshot* from_file(const char* filename); diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index ea13f002..0cba327c 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -36,7 +36,7 @@ namespace ink::runtime::internal return _file + get_offset(idx + 1); } - size_t num_runners() const { return _header.num_runners; } + size_t num_runners() override const { return _header.num_runners; } private: // file information diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 1f87196e..89613ef1 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -20,7 +20,7 @@ void usage() cout << "Usage: inkcpp_cl \n" << "\t-o :\tOutput file name\n" - << "\t-p []:\tPlay mode\n\toptional snapshot file to load\n" + << "\t-p []:\tPlay mode\n\toptional snapshot file to load\n\tto create a snapshot file enter '-1' as choice\n" << endl; } @@ -181,8 +181,7 @@ int main(int argc, const char** argv) std::cin >> c; if (c == -1) { snapshot* snap = thread->create_snapshot(); - // snap->write_to_file("test.snap"); - snap->write_to_file("test.snap"); + snap->write_to_file(std::regex_replace(inputFilename, std::regex("\\.[^\\.]+$"), ".snap").c_str()); break; } thread->choose(c - 1); From aa21c1949a17a1b940b6f1824abb2c6d3ea081a5 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 13:06:26 +0200 Subject: [PATCH 07/14] fix wrong order of const ovrride --- inkcpp/snapshot_impl.h | 2 +- inkcpp_test/NewLines.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index 0cba327c..4dc5acda 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -36,7 +36,7 @@ namespace ink::runtime::internal return _file + get_offset(idx + 1); } - size_t num_runners() override const { return _header.num_runners; } + size_t num_runners() const override { return _header.num_runners; } private: // file information diff --git a/inkcpp_test/NewLines.cpp b/inkcpp_test/NewLines.cpp index adaa078c..910dfa88 100644 --- a/inkcpp_test/NewLines.cpp +++ b/inkcpp_test/NewLines.cpp @@ -2,6 +2,7 @@ #include "../inkcpp_cl/test.h" #include +#include #include #include From 1ad5406cc63305ea9afd69d952e7040de9ac270d Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 13:41:20 +0200 Subject: [PATCH 08/14] Fix access to union to conform with mvsc --- inkcpp/value.cpp | 6 +++--- inkcpp/value.h | 14 +------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index 38bd7ffb..ade47708 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -76,7 +76,7 @@ namespace ink::runtime::internal unsigned char* ptr = data; ptr = snap_write(ptr, _type, data); if (_type == value_type::string) { - unsigned char buf[max_value_size]; + unsigned char buf[max_value_size()]; string_type* res = reinterpret_cast(buf); auto str = get(); res->allocated = str.allocated; @@ -88,14 +88,14 @@ namespace ink::runtime::internal ptr = snap_write(ptr, buf, data); } else { // TODO more space efficent? - ptr = snap_write(ptr, &bool_value, max_value_size, data); + ptr = snap_write(ptr, &bool_value, max_value_size(), data); } return ptr - data; } const unsigned char* value::snap_load(const unsigned char* ptr, const loader& loader) { ptr = snap_read(ptr, _type); - ptr = snap_read(ptr, &bool_value, max_value_size); + ptr = snap_read(ptr, &bool_value, max_value_size()); if(_type == value_type::string) { if(string_value.allocated) { string_value.str = loader.string_table[(std::uintptr_t)(string_value.str)]; diff --git a/inkcpp/value.h b/inkcpp/value.h index 3415b55c..ef973519 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,19 +165,7 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size = - sizeof_largest_type< - decltype(bool_value), - decltype(int32_value), - decltype(string_value), - decltype(uint32_value), - decltype(float_value), - decltype(jump), - decltype(list_value), - decltype(list_flag_value), - decltype(frame_value), - decltype(pointer) - >(); + static constexpr size_t max_value_size() { return sizeof(value) - sizeof(value_type); } value_type _type; }; From 888c39a1728ea3f903d6ed6580740d734354ee9f Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 14:24:37 +0200 Subject: [PATCH 09/14] TEST commit --- inkcpp/value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkcpp/value.h b/inkcpp/value.h index ef973519..333f943b 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,7 +165,7 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size() { return sizeof(value) - sizeof(value_type); } + static constexpr size_t max_value_size() { return 128; } value_type _type; }; From 4e151805e6134a212bcb59170c88b7cd663fa6ff Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 14:34:31 +0200 Subject: [PATCH 10/14] Fiddling with msvc error --- inkcpp/value.cpp | 6 +++--- inkcpp/value.h | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index ade47708..38bd7ffb 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -76,7 +76,7 @@ namespace ink::runtime::internal unsigned char* ptr = data; ptr = snap_write(ptr, _type, data); if (_type == value_type::string) { - unsigned char buf[max_value_size()]; + unsigned char buf[max_value_size]; string_type* res = reinterpret_cast(buf); auto str = get(); res->allocated = str.allocated; @@ -88,14 +88,14 @@ namespace ink::runtime::internal ptr = snap_write(ptr, buf, data); } else { // TODO more space efficent? - ptr = snap_write(ptr, &bool_value, max_value_size(), data); + ptr = snap_write(ptr, &bool_value, max_value_size, data); } return ptr - data; } const unsigned char* value::snap_load(const unsigned char* ptr, const loader& loader) { ptr = snap_read(ptr, _type); - ptr = snap_read(ptr, &bool_value, max_value_size()); + ptr = snap_read(ptr, &bool_value, max_value_size); if(_type == value_type::string) { if(string_value.allocated) { string_value.str = loader.string_table[(std::uintptr_t)(string_value.str)]; diff --git a/inkcpp/value.h b/inkcpp/value.h index 333f943b..2c3b848e 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -92,7 +92,7 @@ namespace ink::runtime::internal { /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; - constexpr value() : _type{value_type::none}, uint32_value{0}{} + constexpr value() : _type{value_type::none}, bool_value{0}{} /// get value of the type (if possible) template @@ -165,7 +165,19 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size() { return 128; } + static constexpr size_t max_value_size = + sizeof_largest_type< + decltype(bool_value), + decltype(int32_value), + decltype(string_value), + decltype(uint32_value), + decltype(float_value), + decltype(jump), + decltype(list_value), + decltype(list_flag_value), + decltype(frame_value), + decltype(pointer) + >(); value_type _type; }; From 779e9350803c8ead5e9a4132169a33223936e055 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 14:42:07 +0200 Subject: [PATCH 11/14] Next try --- inkcpp/value.h | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/inkcpp/value.h b/inkcpp/value.h index 2c3b848e..e1a429b5 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,19 +165,7 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size = - sizeof_largest_type< - decltype(bool_value), - decltype(int32_value), - decltype(string_value), - decltype(uint32_value), - decltype(float_value), - decltype(jump), - decltype(list_value), - decltype(list_flag_value), - decltype(frame_value), - decltype(pointer) - >(); + static constexpr size_t max_value_size() { return 120; } value_type _type; }; From 8ac52e5c1c4d0094cf321c6b5044542f1ec25fe8 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 14:43:44 +0200 Subject: [PATCH 12/14] Next --- inkcpp/value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkcpp/value.h b/inkcpp/value.h index e1a429b5..f01c79d7 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,7 +165,7 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size() { return 120; } + static constexpr size_t max_value_size = 128; value_type _type; }; From bd5f48fa5c0a22a9966fa0b7a9ec15d5f67d7c13 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 17:16:19 +0200 Subject: [PATCH 13/14] More try --- inkcpp/snapshot_interface.h | 2 ++ inkcpp/value.h | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/inkcpp/snapshot_interface.h b/inkcpp/snapshot_interface.h index 12c6436f..1408124b 100644 --- a/inkcpp/snapshot_interface.h +++ b/inkcpp/snapshot_interface.h @@ -10,8 +10,10 @@ namespace ink::runtime::internal class string_table; template class managed_array; + class snapshot_interface { public: + constexpr snapshot_interface(){}; static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) { if (write) { memcpy(ptr, data, length); } diff --git a/inkcpp/value.h b/inkcpp/value.h index f01c79d7..212b212e 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -92,7 +92,7 @@ namespace ink::runtime::internal { /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; - constexpr value() : _type{value_type::none}, bool_value{0}{} + constexpr value() : snapshot_interface(), _type{value_type::none}, bool_value{0}{} /// get value of the type (if possible) template @@ -165,7 +165,19 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size = 128; + static constexpr size_t max_value_size = + sizeof_largest_type< + decltype(bool_value), + decltype(int32_value), + decltype(string_value), + decltype(uint32_value), + decltype(float_value), + decltype(jump), + decltype(list_value), + decltype(list_flag_value), + decltype(frame_value), + decltype(pointer) + >(); value_type _type; }; From 045c300e0c3e975b677fe33c30f2c75d4bec60cb Mon Sep 17 00:00:00 2001 From: JBenda Date: Thu, 14 Jul 2022 17:19:11 +0200 Subject: [PATCH 14/14] Adapt to msvc for c++17 --- inkcpp/snapshot_impl.cpp | 4 ++-- inkcpp/story_impl.cpp | 9 ++++----- inkcpp/value.cpp | 4 ++-- inkcpp/value.h | 13 +++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/inkcpp/snapshot_impl.cpp b/inkcpp/snapshot_impl.cpp index 28c6c179..c819929b 100644 --- a/inkcpp/snapshot_impl.cpp +++ b/inkcpp/snapshot_impl.cpp @@ -61,8 +61,8 @@ namespace ink::runtime::internal : _managed{true} { snapshot_interface::snapper snapper{ - .strings = globals.strings(), - .story_string_table = globals._owner->string(0) + globals.strings(), + globals._owner->string(0) }; _length = globals.snap(nullptr, snapper); size_t runner_cnt = 0; diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 147903bb..1904f08b 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -182,9 +182,8 @@ namespace ink::runtime::internal const snapshot_impl& snapshot = reinterpret_cast(data); auto* globs = new globals_impl(this); auto end = globs->snap_load(snapshot.get_globals_snap(), snapshot_interface::loader{ - .string_table = snapshot.strings(), - .story_string_table = _string_table, - + snapshot.strings(), + _string_table, }); inkAssert(end == snapshot.get_runner_snap(0), "not all data were used for global reconstruction"); return globals(globs, _block); @@ -205,8 +204,8 @@ namespace ink::runtime::internal auto* run = new runner_impl(this, store); auto end = run->snap_load(snapshot.get_runner_snap(idx), snapshot_interface::loader{ - .string_table = snapshot.strings(), - .story_string_table = _string_table, + snapshot.strings(), + _string_table, }); inkAssert( (idx + 1 < snapshot.num_runners() && end == snapshot.get_runner_snap(idx + 1)) diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index 38bd7ffb..0f8240db 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -81,9 +81,9 @@ namespace ink::runtime::internal auto str = get(); res->allocated = str.allocated; if (str.allocated) { - res->str = reinterpret_cast(snapper.strings.get_id(str.str)); + res->str = reinterpret_cast(static_cast(snapper.strings.get_id(str.str))); } else { - res->str = reinterpret_cast(str.str - snapper.story_string_table); + res->str = reinterpret_cast(static_cast(str.str - snapper.story_string_table)); } ptr = snap_write(ptr, buf, data); } else { diff --git a/inkcpp/value.h b/inkcpp/value.h index 212b212e..47a12a05 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -93,6 +93,7 @@ namespace ink::runtime::internal { template struct ret { using type = void; }; constexpr value() : snapshot_interface(), _type{value_type::none}, bool_value{0}{} + constexpr explicit value( value_type type ) : _type{ type }, bool_value{0} {} /// get value of the type (if possible) template @@ -434,11 +435,11 @@ namespace ink::runtime::internal { // static constexpr instantiations of flag values namespace values { - static constexpr value marker = value{}.set(); - static constexpr value glue = value{}.set(); - static constexpr value newline = value{}.set(); - static constexpr value func_start = value{}.set(); - static constexpr value func_end = value{}.set(); - static constexpr value null = value{}.set(); + static constexpr value marker = value( value_type::marker ); + static constexpr value glue = value( value_type::glue ); + static constexpr value newline = value( value_type::newline ); + static constexpr value func_start = value( value_type::func_start ); + static constexpr value func_end = value( value_type::func_end ); + static constexpr value null = value( value_type::null ); } }