1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
/*
* This source code is released into the public domain.
*/
module;
#include <algorithm>
#include <coroutine>
#include <expected>
#include <format>
#include <functional>
#include <ranges>
#include <string>
#include <unordered_map>
#include <unordered_set>
module liblfvm;
import nihil;
namespace lfvm {
/*
* Split an option into name and value.
*/
[[nodiscard]] auto parse_option(std::string_view str)
-> std::expected<std::pair<std::string_view, std::string_view>,
nihil::error>
{
auto split = std::ranges::find(str, '=');
if (split == str.end())
return std::unexpected(nihil::error(
"missing '=' in option string"));
auto option = std::string_view(str.begin(), split);
if (option.empty())
return std::unexpected(nihil::error(
"invalid option name"));
auto value = std::string_view(std::next(split), str.end());
if (value.empty())
return std::unexpected(nihil::error(
"invalid option value"));
return std::make_pair(option, value);
}
/*
* Parse a boolean option (true/false).
*/
[[nodiscard]] auto parse_bool(std::string_view value)
-> std::expected<bool, nihil::error>
{
using namespace std::literals;
static auto true_values = std::unordered_set{
"yes"sv, "true"sv, "enabled"sv, "on"sv
};
static auto false_values = std::unordered_set{
"no"sv, "false"sv, "disabled"sv, "off"sv
};
if (true_values.find(value) != true_values.end())
return true;
if (false_values.find(value) != false_values.end())
return false;
return std::unexpected(nihil::error("invalid value for boolean"));
}
auto set_vm_option(vm_config &vm, std::string_view option_string)
-> std::expected<void, nihil::error>
{
using namespace std::literals;
// The table of options we know how to set.
static auto options = std::unordered_map<
std::string_view,
std::function<
std::expected<void, nihil::error>(
vm_config &, std::string_view)
>
> {
std::make_pair("memory"sv,
[](vm_config &vm, std::string_view str) -> std::expected<void, nihil::error> {
// Due to limitations in UCL, the memory size is limited to
// int64_t. In practice, this is not an issue.
auto new_size = co_await nihil::parse_size<std::int64_t>(str);
co_await vm.memory_size(new_size);
co_return {};
}),
std::make_pair("ncpus"sv,
[](vm_config &vm, std::string_view str) -> std::expected<void, nihil::error> {
auto ncpus = co_await nihil::parse_size<unsigned>(str);
co_await vm.ncpus(ncpus);
co_return {};
}),
std::make_pair("wire_memory"sv,
[](vm_config &vm, std::string_view str) -> std::expected<void, nihil::error> {
auto b = co_await parse_bool(str);
vm.wire_memory(b);
co_return {};
}),
std::make_pair("include_memory_in_core"sv,
[](vm_config &vm, std::string_view str) -> std::expected<void, nihil::error> {
auto b = co_await parse_bool(str);
vm.include_memory_in_core(b);
co_return {};
}),
};
auto [name, value] = co_await parse_option(option_string);
auto option = options.find(name);
if (option == options.end())
co_return std::unexpected(nihil::error(
std::format("unknown option '{}'", name)));
co_await option->second(vm, value).transform_error(
[&](nihil::error cause) {
return nihil::error(
std::format("failed to set {}", name),
std::move(cause));
});
co_return {};
}
} // namespace lfvm
|