Skip to content

Commit 06abf63

Browse files
authored
[#114] feat(DaedalusVm): introduce dynamically growing temporary strings
1 parent 54b826a commit 06abf63

4 files changed

Lines changed: 78 additions & 2 deletions

File tree

include/zenkit/DaedalusScript.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,8 @@ namespace zenkit {
667667
return *_m_registered_to;
668668
}
669669

670+
ZKAPI void grow(uint32_t n);
671+
670672
protected:
671673
template <typename T>
672674
T const* get_member_ptr(std::uint16_t index, DaedalusInstance const* context) const {

include/zenkit/DaedalusVm.hh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,12 @@ namespace zenkit {
973973
}
974974
}
975975

976+
/// \brief Mark the temporary string at the given index as free.
977+
void mark_free_string(uint32_t index);
978+
979+
/// \brief Obtain a random free temporary string index.
980+
uint32_t get_free_string();
981+
976982
private:
977983
std::array<DaedalusStackFrame, stack_size> _m_stack;
978984
uint16_t _m_stack_ptr {0};
@@ -993,6 +999,7 @@ namespace zenkit {
993999
DaedalusSymbol* _m_item_sym;
9941000

9951001
DaedalusSymbol* _m_temporary_strings;
1002+
std::stack<uint32_t> _m_temporary_strings_free {};
9961003

9971004
std::shared_ptr<DaedalusInstance> _m_instance;
9981005
std::uint32_t _m_pc {0};

src/DaedalusScript.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,39 @@ namespace zenkit {
570570
std::get<std::shared_ptr<DaedalusInstance>>(_m_value) = inst;
571571
}
572572

573+
void DaedalusSymbol::grow(uint32_t n) {
574+
auto& value = this->_m_value;
575+
576+
if (std::holds_alternative<std::unique_ptr<std::string[]>>(value)) {
577+
auto new_value = std::make_unique<std::string[]>(this->_m_count + n);
578+
auto& old_value = std::get<std::unique_ptr<std::string[]>>(value);
579+
580+
// Move strings from the current value into the new, larger array.
581+
for (uint32_t i = 0; i < this->_m_count; ++i) {
582+
new_value[i] = std::move(old_value[i]);
583+
}
584+
585+
this->_m_value = std::move(new_value);
586+
} else if (std::holds_alternative<std::unique_ptr<std::int32_t[]>>(value)) {
587+
auto new_value = std::make_unique<std::int32_t[]>(this->_m_count + n);
588+
auto& old_value = std::get<std::unique_ptr<std::int32_t[]>>(value);
589+
590+
std::copy_n(&old_value[0], this->_m_count, &new_value[0]);
591+
this->_m_value = std::move(new_value);
592+
} else if (std::holds_alternative<std::unique_ptr<float[]>>(value)) {
593+
auto new_value = std::make_unique<float[]>(this->_m_count + n);
594+
auto& old_value = std::get<std::unique_ptr<float[]>>(value);
595+
596+
std::copy_n(&old_value[0], this->_m_count, &new_value[0]);
597+
this->_m_value = std::move(new_value);
598+
} else {
599+
// Cannot grow other symbol types.
600+
throw DaedalusIllegalTypeAccess(this, this->type());
601+
}
602+
603+
this->_m_count = this->_m_count + n;
604+
}
605+
573606
void DaedalusSymbol::set_access_trap_enable(bool enable) noexcept {
574607
if (enable)
575608
_m_flags |= DaedalusSymbolFlag::TRAP_ACCESS;

src/DaedalusVm.cc

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ namespace zenkit {
7777

7878
DaedalusVm::DaedalusVm(DaedalusScript&& scr, std::uint8_t flags) : DaedalusScript(std::move(scr)), _m_flags(flags) {
7979
_m_temporary_strings = add_temporary_strings_symbol();
80+
_m_temporary_strings_free.push(0);
81+
8082
_m_self_sym = find_symbol_by_name("SELF");
8183
_m_other_sym = find_symbol_by_name("OTHER");
8284
_m_victim_sym = find_symbol_by_name("VICTIM");
@@ -508,8 +510,12 @@ namespace zenkit {
508510
}
509511

510512
void DaedalusVm::push_string(std::string_view value) {
511-
_m_temporary_strings->set_string(value);
512-
push_reference(_m_temporary_strings);
513+
// We need to push strings without an explicitly attached variable to a
514+
// virtual string container variable.
515+
auto index = this->get_free_string();
516+
517+
_m_temporary_strings->set_string(value, index);
518+
push_reference(_m_temporary_strings, index);
513519
}
514520

515521
void DaedalusVm::push_float(float value) {
@@ -584,6 +590,13 @@ namespace zenkit {
584590
throw DaedalusVmException {"tried to pop_reference but frame does not contain a reference."};
585591
}
586592

593+
auto sym = std::get<DaedalusSymbol*>(v.value);
594+
if (sym->index() == _m_temporary_strings->index()) {
595+
// For virtual strings, we'll mark this string as free when it's popped.
596+
// This is likely unreliable but until it causes issues, it'll stay as-is.
597+
this->mark_free_string(v.index);
598+
}
599+
587600
return {std::get<DaedalusSymbol*>(v.value), v.index, v.context};
588601
}
589602

@@ -981,4 +994,25 @@ namespace zenkit {
981994
ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", ref->name().c_str());
982995
}
983996
}
997+
998+
void DaedalusVm::mark_free_string(uint32_t index) {
999+
this->_m_temporary_strings_free.push(index);
1000+
}
1001+
1002+
uint32_t DaedalusVm::get_free_string() {
1003+
if (_m_temporary_strings_free.size() == 0) {
1004+
// Like a normal array, we'll grow exponentially.
1005+
auto prev_size = _m_temporary_strings->count();
1006+
_m_temporary_strings->grow(prev_size * 2);
1007+
1008+
// Mark new strings as unused.
1009+
for (uint32_t i = prev_size; i < _m_temporary_strings->count(); ++i) {
1010+
_m_temporary_strings_free.push(i);
1011+
}
1012+
}
1013+
1014+
auto index = this->_m_temporary_strings_free.top();
1015+
this->_m_temporary_strings_free.pop();
1016+
return index;
1017+
}
9841018
} // namespace zenkit

0 commit comments

Comments
 (0)