13bool parseSegment(
const std::string& seg, std::string&
key, std::vector<size_t>& indexes) {
16 if (seg.empty())
return false;
20 while (i < seg.size() && seg[i] !=
'[')
key.push_back(seg[i++]);
22 if (
key.empty() && (i >= seg.size()))
return false;
24 while (i < seg.size()) {
25 if (seg[i] !=
'[')
return false;
26 const size_t close = seg.find(
']', i);
27 if (close == std::string::npos)
return false;
28 const std::string
idx = seg.substr(i + 1, close - i - 1);
29 if (
idx.empty() ||
idx.find_first_not_of(
"0123456789") != std::string::npos)
return false;
30 indexes.push_back(
static_cast<size_t>(std::stoull(
idx)));
65 for (
auto& kv : obj_) {
66 if (kv.first ==
key) {
67 kv.second = std::move(
v);
71 obj_.emplace_back(
key, std::move(
v));
75 for (
const auto& kv : obj_)
76 if (kv.first ==
key)
return &kv.second;
82 if (kv.first ==
key)
return &kv.second;
87 std::vector<std::string> out;
88 out.reserve(obj_.size());
89 for (
const auto& kv : obj_) out.push_back(kv.first);
97 const size_t dot = dottedPath.find(
'.',
start);
98 const bool last = (dot == std::string::npos);
99 const std::string seg = dottedPath.substr(
start, last ? std::string::npos : dot -
start);
102 std::vector<size_t> indexes;
103 if (!parseSegment(seg,
key, indexes))
return nullptr;
106 if (!cur)
return nullptr;
108 for (
const size_t idx : indexes) {
109 if (!cur->
isArray() ||
idx >= cur->arr_.size())
return nullptr;
110 cur = &cur->arr_[
idx];
112 if (last)
return cur;
122 std::vector<std::string> segs;
125 const size_t dot = dottedPath.find(
'.',
start);
126 if (dot == std::string::npos) {
127 segs.push_back(dottedPath.substr(
start));
130 segs.push_back(dottedPath.substr(
start, dot -
start));
133 if (segs.empty() || (segs.size() == 1 && segs[0].empty()))
return false;
136 for (
size_t si = 0; si + 1 < segs.size(); ++si) {
138 std::vector<size_t> indexes;
141 if (!parseSegment(segs[si],
key, indexes) ||
key.empty() || !indexes.empty())
return false;
152 std::vector<size_t> indexes;
153 if (!parseSegment(segs.back(),
key, indexes))
return false;
154 if (!indexes.empty()) {
158 if (!arr || !arr->
isArray())
return false;
161 if (!cur->
isArray() || indexes.size() != 1 || indexes[0] >= cur->arr_.size())
return false;
162 cur->arr_[indexes[0]] = std::move(
v);
172 bool changed =
false;
173 for (
const auto& kv : defaults.obj_) {
176 set(kv.first, kv.second);
178 }
else if (mine->
isObject() && kv.second.isObject()) {
186 if (kind_ != o.kind_)
return false;
JSON-compatible state value tree used by state hot reload.
static StateValue object()
Empty object.
static StateValue boolean(bool v)
Boolean value.
bool setPath(const std::string &dottedPath, StateValue v)
Set a value at a dotted path, creating missing intermediate objects along the way.
static StateValue integer(int64_t v)
Integer value.
bool mergeDefaults(const StateValue &defaults)
Fill missing members from defaults; object members merge recursively, existing values are never overw...
void pushBack(StateValue v)
Append an element; only valid on arrays.
const StateValue * get(const std::string &dottedPath) const
Navigate a dotted path ("a.b[2].c").
bool operator==(const StateValue &o) const
static StateValue number(double v)
Floating-point value.
const StateValue * find(const std::string &key) const
Look up key; nullptr when absent.
std::vector< std::string > keys() const
Member names in insertion order.
static StateValue string(std::string v)
String value.
void set(const std::string &key, StateValue v)
Insert or replace key; only valid on objects.
Build metadata (engine git commit, build time, third-party version).