mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 00:15:32 +00:00
fix(windows): change option_stack to deque
when vectors are resized the memory is realocated so all the option pointers become invalid. Using a deque avoids this it has all the same methods so it is straight replacement. fixes: #15961
This commit is contained in:
parent
01e101babc
commit
a07fff8be3
4 changed files with 71 additions and 38 deletions
|
|
@ -130,7 +130,9 @@ namespace km {
|
|||
}
|
||||
// For this mock processor we only support queuing PERSIST_OPT action items.
|
||||
if (action_item->type == KM_CORE_IT_PERSIST_OPT && action_item->option) {
|
||||
state->actions().push_persist(update_option(static_cast<km_core_option_scope>(action_item->option->scope), action_item->option->key, action_item->option->value));
|
||||
state->actions().push_persist(update_option(static_cast<km_core_option_scope>(action_item->option->scope),
|
||||
action_item->option->key,
|
||||
action_item->option->value));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
|
@ -175,6 +177,18 @@ namespace km {
|
|||
u"F2 pressed test save."));
|
||||
break;
|
||||
}
|
||||
case KM_CORE_VKEY_F3:
|
||||
{
|
||||
state->actions().push_persist(
|
||||
update_option(KM_CORE_OPT_KEYBOARD,
|
||||
u"__test_point_3",
|
||||
u"F3 pressed test save 1."));
|
||||
state->actions().push_persist(
|
||||
update_option(KM_CORE_OPT_KEYBOARD,
|
||||
u"__test_point_4",
|
||||
u"F3 pressed test save 2."));
|
||||
break;
|
||||
}
|
||||
|
||||
case KM_CORE_VKEY_F4:
|
||||
state->context().push_marker(KM_CORE_VKEY_QUOTE);
|
||||
|
|
|
|||
|
|
@ -40,17 +40,16 @@ actions::actions(actions const &other)
|
|||
: std::vector<action>(other)
|
||||
, _option_items_stack(other._option_items_stack)
|
||||
{
|
||||
// Update all option pointers to point to the new stack
|
||||
// Update all option pointers to point to the new stack.
|
||||
|
||||
size_t opt_index = 0;
|
||||
for (auto &item : *this) {
|
||||
if (item.type == KM_CORE_IT_PERSIST_OPT && item.option) {
|
||||
// Find the corresponding option in the new stack
|
||||
// The pointers in the original point to positions in other._option_items_stack
|
||||
// We need to find the equivalent position in our _option_items_stack
|
||||
auto original_ptr = item.option;
|
||||
auto original_base = reinterpret_cast<km_core_option_item const *>(other._option_items_stack.data());
|
||||
auto offset = original_ptr - original_base;
|
||||
if (offset >= 0 && static_cast<size_t>(offset) < _option_items_stack.size()) {
|
||||
item.option = &_option_items_stack[offset];
|
||||
if (item.type == KM_CORE_IT_PERSIST_OPT) {
|
||||
if (opt_index < _option_items_stack.size()) {
|
||||
item.option = &_option_items_stack[opt_index++];
|
||||
} else {
|
||||
// no matching item in the stack; clear pointer.
|
||||
item.option = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "keyman_core.h"
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ using action = km_core_action_item;
|
|||
|
||||
class actions : public std::vector<action>
|
||||
{
|
||||
std::vector<option> _option_items_stack;
|
||||
std::deque<option> _option_items_stack;
|
||||
|
||||
template<km_core_action_type V>
|
||||
void _push_vkey(km_core_virtual_key);
|
||||
|
|
|
|||
|
|
@ -251,11 +251,23 @@ constexpr km_core_option_item const expected_persist_opt = {
|
|||
KM_CORE_OPT_KEYBOARD
|
||||
};
|
||||
|
||||
//constexpr km_core_option_item const clone_persist_opt = {
|
||||
// u"__test_clone",
|
||||
// u"Not in original",
|
||||
// KM_CORE_OPT_KEYBOARD
|
||||
//};
|
||||
constexpr km_core_option_item const clone_persist_opt = {
|
||||
u"__test_clone",
|
||||
u"Not in original",
|
||||
KM_CORE_OPT_KEYBOARD
|
||||
};
|
||||
|
||||
constexpr km_core_option_item const test_point_3_opt = {
|
||||
u"__test_point_3",
|
||||
u"F3 pressed test save 1.",
|
||||
KM_CORE_OPT_KEYBOARD
|
||||
};
|
||||
|
||||
constexpr km_core_option_item const test_point_4_opt = {
|
||||
u"__test_point_4",
|
||||
u"F3 pressed test save 2.",
|
||||
KM_CORE_OPT_KEYBOARD
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
|
@ -275,8 +287,8 @@ int main(int argc, char * argv[])
|
|||
|
||||
km_core_keyboard * test_kb = nullptr;
|
||||
km_core_state * test_state = nullptr,
|
||||
* test_clone = nullptr;
|
||||
// * test_clone2 = nullptr;
|
||||
* test_clone = nullptr,
|
||||
* test_clone_2 = nullptr;
|
||||
test_kb = (km_core_keyboard *)new km::core::mock_processor(km::core::path("dummy.mock"));
|
||||
|
||||
// Simple sanity tests.
|
||||
|
|
@ -401,34 +413,41 @@ int main(int argc, char * argv[])
|
|||
clone_state_deleted_text
|
||||
));
|
||||
|
||||
// Need to test the option pointer is pointing to the correct the correct _option_items_stack.
|
||||
// This could not be tested like this as the way commit() works adding KM_CORE_IT_END to the
|
||||
// end of the actions list. This means when calling km_core_state_queue_action_items
|
||||
// I get an assert in push_persist "empty() || back().type != KM_CORE_IT_END" it is a protection
|
||||
// against adding a action to an already commited list.
|
||||
// To test this change I need to do mocking closer to the integration of that actions list.
|
||||
// Add two actions before cloning the state again
|
||||
try_status(km_core_process_event(test_state, KM_CORE_VKEY_F3, 0, 1, KM_CORE_EVENT_FLAG_DEFAULT));
|
||||
try_status(km_core_state_clone(test_state, &test_clone_2));
|
||||
|
||||
// Now put an option in the test_clone_2 state only
|
||||
km_core_action_item action_clone = {KM_CORE_IT_PERSIST_OPT, {0,}, };
|
||||
action_clone.option = &clone_persist_opt;
|
||||
if (test_clone_2->actions().back().type == KM_CORE_IT_END) {
|
||||
test_clone_2->actions().pop_back();
|
||||
}
|
||||
km_core_state_queue_action_items(test_clone_2, &action_clone);
|
||||
test_clone_2->actions().commit();
|
||||
|
||||
// Test debug dump
|
||||
auto doc3 = get_json_doc(*test_state), doc4 = get_json_doc(*test_clone_2);
|
||||
std::cout << "doc3:" << std::endl;
|
||||
std::cout << doc3 << std::endl;
|
||||
std::cout << "doc4:" << std::endl;
|
||||
std::cout << doc4 << std::endl;
|
||||
if (doc3 == doc4) return __LINE__;
|
||||
|
||||
// km_core_action_item actions_test[] = {
|
||||
// { KM_CORE_IT_PERSIST_OPT, {0,}, },
|
||||
// { KM_CORE_IT_END, {0,}, }
|
||||
// };
|
||||
km_core_action_item action_tp3 = {KM_CORE_IT_PERSIST_OPT, {0,}, };
|
||||
action_tp3.option = &test_point_3_opt;
|
||||
|
||||
// actions_test[0].option = &clone_persist_opt;
|
||||
km_core_action_item action_tp4 = {KM_CORE_IT_PERSIST_OPT, {0,}, };
|
||||
action_tp4.option = &test_point_4_opt;
|
||||
|
||||
//try_status(km_core_state_create(test_kb, test_env_opts, &test_clone2));
|
||||
|
||||
|
||||
//km_core_state_queue_action_items(test_clone2, actions_test);
|
||||
//test_assert(action_items(test_clone2, actions_test));
|
||||
|
||||
//test_assert(action_items(test_clone2, {action_clone, {KM_CORE_IT_END}}));
|
||||
test_assert(action_items(test_state, {action_tp3, action_tp4, {KM_CORE_IT_END}}));
|
||||
// Check that test_clone_2 has the same persisted options plus the extra queued option.
|
||||
test_assert(action_items(test_clone_2, {action_tp3, action_tp4, action_clone, {KM_CORE_IT_END}}));
|
||||
|
||||
// Destroy them
|
||||
km_core_state_dispose(test_state);
|
||||
km_core_state_dispose(test_clone);
|
||||
//km_core_state_dispose(test_clone2);
|
||||
km_core_state_dispose(test_clone_2);
|
||||
km_core_keyboard_dispose(test_kb);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue